我想验证 C# 中包含带括号的布尔表达式的字符串。该字符串应仅包含数字 1-9、圆括号、“OR”、“AND”。
好字符串的例子:
“1 和 2”
“2 或 4”
“4 与(3 或 5)”
“2”
等等...
我不确定正则表达式是否足够灵活来完成这项任务。在 C# 中是否有一个很好的捷径来实现这一点?
我想验证 C# 中包含带括号的布尔表达式的字符串。该字符串应仅包含数字 1-9、圆括号、“OR”、“AND”。
好字符串的例子:
“1 和 2”
“2 或 4”
“4 与(3 或 5)”
“2”
等等...
我不确定正则表达式是否足够灵活来完成这项任务。在 C# 中是否有一个很好的捷径来实现这一点?
使用简单的解析器可能更简单。但是您可以使用 .NET 正则表达式来做到这一点,方法是使用平衡组并意识到如果从字符串中删除括号,您总是有一个与简单表达式匹配的字符串,例如^\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: ''
如果您只想验证输入字符串,您可以编写一个简单的解析器。每个方法都使用某种输入(数字、括号、运算符)并在匹配后返回剩余的字符串。如果无法进行匹配,则会引发异常。
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();
};
}
}
如果您认为布尔表达式是由正式语法生成的,那么编写解析器会更容易。
我制作了一个开源库来解释简单的布尔表达式。您可以在GitHub 上查看它,特别是查看AstParser
类和Lexer
.
很简单:
在第一阶段,您必须通过简单的字符串比较来确定词法(数字、括号或运算符)。
在第二阶段,您必须定义闭括号计数变量(bracketPairs),可以通过以下算法为每个词法计算:
如果当前词法是'(',则括号对++;
如果当前词法是')',那么括号对--。
否则不要修改bracketPairs。
最后,如果所有词法都已知且括号对 == 0,则输入表达式有效。
如果需要构建 AST,任务会稍微复杂一些。
你想要的是“平衡组”,有了它们你可以得到所有的括号定义,那么你只需要一个简单的字符串解析
http://blog.stevenlevithan.com/archives/balancing-groups
http://msdn.microsoft.com/en-us/library/bs2twtah.aspx#balancing_group_definition