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

时间:2012-09-23 14:54:09

标签: c# regex parsing boolean boolean-logic

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

良好字符串的示例:

“1 AND 2”

“2 OR 4”

“4 AND(3 OR 5)”

“2”

等等......

我不确定正则表达式是否足够灵活用于此任务。 在C#中有一个很好的简短方法吗?

6 个答案:

答案 0 :(得分:5)

使用简单的解析器执行此操作可能更简单。但是使用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)使正则表达式引擎忽略模式中的所有空格和注释,因此可以使其更具可读性。)

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:  ''

答案 1 :(得分:1)

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

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();
        };
    }
}

答案 2 :(得分:0)

ANTLER解析器生成器?

  

在C#中实现这一目标的简短方法

虽然如果只是数字和OR + AND

可能会有些过分

答案 3 :(得分:0)

很简单:

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

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

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

如果当前lexem是')',那么supportPairs - 。

否则不要修改bracketPairs。

如果所有的lexems都已知并且bracketPairs == 0,那么输入表达式是有效的。

如果建立AST,那么任务就更复杂了。

答案 4 :(得分:0)

你想要的是“平衡群体”,用它们你可以得到所有的手镯定义,然后你只需要一个简单的字符串解析

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

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

答案 5 :(得分:0)

如果您考虑由形式语法生成的布尔表达式,则更容易编写解析器

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