在 C# 中使用括号验证布尔表达式

Posted

技术标签:

【中文标题】在 C# 中使用括号验证布尔表达式【英文标题】:Validate a Boolean expression with brackets in C# 【发布时间】:2012-09-15 05:21:48 【问题描述】:

我想验证 C# 中包含带括号的布尔表达式的字符串。 字符串只能包含数字 1-9、圆括号、“OR”、“AND”。

好字符串的例子:

“1 和 2”

“2 或 4”

“4 与(3 或 5)”

“2”

等等……

我不确定正则表达式是否足够灵活来完成这项任务。 在 C# 中是否有一个很好的捷径来实现这一点?

【问题讨论】:

这种组合也可以吗? 4 AND 5 (3 OR 5) OR (4 AND 6) 或 etcs.. @John Woo No... 在前 5 个之后,没有运算符。但是,如果您的意思是包含多种组合的长表达式,那么可以。 大括号匹配是常规语言无法做到的典型例子。 @harold,.NET 正则表达式不规则。 【参考方案1】:

使用简单的解析器可能更简单。但是您 can do this 使用 .NET 正则表达式通过使用 balancing groups 并意识到如果从字符串中删除括号,您总是有一个字符串与 ^\d+(?:\s+(?:AND|OR)\s+\d+)*\z 之类的简单表达式匹配。

所以你所要做的就是使用平衡组来确保括号是平衡的(并且在正确的位置以正确的形式)。

稍微改写一下上面的表达式:

(?x)^
OPENING
\d+
CLOSING
(?:
    \s+(?:AND|OR)\s+
    OPENING
    \d+
    CLOSING
)*
BALANCED
\z

(?x) 使正则表达式引擎忽略模式中的所有空格和 cmets,因此可以使其更具可读性。)

OPENING 匹配任意数量(包括 0)的左括号:

\s* (?: (?<open> \( ) \s* )*

CLOSING 匹配任意数量的右括号,同时确保平衡组是平衡的:

\s* (?: (?<-open> \) ) \s* )*

BALANCED 执行平衡检查,如果有更多的开括号然后闭合则失败:

(?(open)(?!))

给出表达式:

(?x)^
\s* (?: (?<open> \( ) \s* )*
\d+
\s* (?: (?<-open> \) ) \s* )*
(?:
    \s+(?:AND|OR)\s+
    \s* (?: (?<open> \( ) \s* )*
    \d+
    \s* (?: (?<-open> \) ) \s* )*
)*
(?(open)(?!))
\z

如果您不想让随机空格删除每个\s*

示例

在IdeOne 上查看演示。输出:

matched: '2'
matched: '1 AND 2'
matched: '12 OR 234'
matched: '(1) AND (2)'
matched: '(((1)) AND (2))'
matched: '1 AND 2 AND 3'
matched: '1 AND (2 OR (3 AND 4))'
matched: '1 AND (2 OR 3) AND 4'
matched: ' ( 1    AND ( 2 OR  ( 3 AND    4 )  )'
matched: '((1 AND 7) OR 6) AND ((2 AND 5) OR (3 AND 4))'
matched: '(1)'
matched: '(((1)))'
failed:  '1 2'
failed:  '1(2)'
failed:  '(1)(2)'
failed:  'AND'
failed:  '1 AND'
failed:  '(1 AND 2'
failed:  '1 AND 2)'
failed:  '1 (AND) 2'
failed:  '(1 AND 2))'
failed:  '(1) AND 2)'
failed:  '(1)() AND (2)'
failed:  '((1 AND 7) OR 6) AND (2 AND 5) OR (3 AND 4))'
failed:  '((1 AND 7) OR 6) AND ((2 AND 5 OR (3 AND 4))'
failed:  ''

【讨论】:

太棒了!这是一些核心正则表达式(-:不知道平衡功能。【参考方案2】:

如果您只想验证输入字符串,您可以编写一个简单的解析器。 每个方法都使用某种输入(数字、括号、运算符)并在匹配后返回剩余的字符串。如果无法匹配,则会引发异常。

public class ParseException : Exception  

public static class ExprValidator

    public static bool Validate(string str)
    
        try
        
            string term = Term(str);
            string stripTrailing = Whitespace(term);

            return stripTrailing.Length == 0;
        
        catch(ParseException)  return false; 
    

    static string Term(string str)
    
        if(str == string.Empty) return str;
        char current = str[0];

        if(current == '(')
        
            string term = LBracket(str);
            string rBracket = Term(term);
            string temp = Whitespace(rBracket);
            return RBracket(temp);
        
        else if(Char.IsDigit(current))
        
            string rest = Digit(str);
            try
            
                //possibly match op term
                string op = Op(rest);
                return Term(op);
            
            catch(ParseException)  return rest; 
        
        else if(Char.IsWhiteSpace(current))
        
            string temp = Whitespace(str);
            return Term(temp);
        
        else throw new ParseException();
    

    static string Op(string str)
    
        string t1 = Whitespace_(str);
        string op = MatchOp(t1);
        return Whitespace_(op);
    

    static string MatchOp(string str)
    
        if(str.StartsWith("AND")) return str.Substring(3);
        else if(str.StartsWith("OR")) return str.Substring(2);
        else throw new ParseException();
    

    static string LBracket(string str)
    
        return MatchChar('(')(str);
    

    static string RBracket(string str)
    
        return MatchChar(')')(str);
    

    static string Digit(string str)
    
        return MatchChar(Char.IsDigit)(str);
    

    static string Whitespace(string str)
    
        if(str == string.Empty) return str;

        int i = 0;
        while(i < str.Length && Char.IsWhiteSpace(str[i]))  i++; 

        return str.Substring(i);
    

    //match at least one whitespace character
    static string Whitespace_(string str)
    
        string stripFirst = MatchChar(Char.IsWhiteSpace)(str);
        return Whitespace(stripFirst);
    

    static Func<string, string> MatchChar(char c)
    
        return MatchChar(chr => chr == c);
    

    static Func<string, string> MatchChar(Func<char, bool> pred)
    
        return input => 
            if(input == string.Empty) throw new ParseException();
            else if(pred(input[0])) return input.Substring(1);
            else throw new ParseException();
        ;
    

【讨论】:

使用正则表达式解决方案更简单。但感谢您的代码! (如果 System.Text.RegularExpressions 由于某种原因不可用,可能会派上用场。)【参考方案3】:

如果您认为布尔表达式是由形式语法生成的,那么编写解析器会更容易。

我制作了一个开源库来解释简单的布尔表达式。您可以在GitHub 上查看它,特别是查看AstParser 类和Lexer

【讨论】:

【参考方案4】:

很简单:

在第一阶段,您必须通过简单的字符串比较来确定词法(数字、括号或运算符)。

在第二阶段,您必须定义闭括号计数变量(bracketPairs),可以通过以下算法为每个词法计算:

如果当前词法是'(',那么bracketPairs++;

如果当前词法是')',那么bracketPairs--.

否则不要修改bracketPairs。

最后,如果所有词法都已知且括号对== 0,则输入表达式有效。

如果需要构建 AST,任务会稍微复杂一些。

【讨论】:

这有效吗? )( 当然不是 :) 该算法有一个注意事项:在每个括号中,您必须检查 bracketPairs 是否大于或等于零以使此类情况无效。【参考方案5】:

你想要的是“平衡组”,有了它们你可以得到所有的括号定义,然后你只需要一个简单的字符串解析

http://blog.stevenlevithan.com/archives/balancing-groups

http://msdn.microsoft.com/en-us/library/bs2twtah.aspx#balancing_group_definition

【讨论】:

【参考方案6】:

ANTLR 解析器生成器?

在 C# 中实现此目的的一种简单方法

虽然如果它只是数字和 OR + AND 可能有点矫枉过正

【讨论】:

ANTLER 看起来有点矫枉过正。我更多地考虑的是一种小方法。

以上是关于在 C# 中使用括号验证布尔表达式的主要内容,如果未能解决你的问题,请参考以下文章

C#中if语句的基本运用

用正则表达式解析布尔算术,包括括号?

C#常用正则表达式回顾

计算带圆括号的布尔表达式(使用堆栈)

Haskell 布尔表达式:解析错误(可能不正确的缩进或不匹配的括号)

&&= 在 C# 中? (布尔表达式)[重复]