您为这项工作使用了错误的工具。 .NET 正则表达式不提供递归,尤其是我不确定它是否能识别所有 CFG,这本身就是一个有趣的问题1。因此,如果您的问题是关于学术兴趣的,那么您可以提前阅读。如果您想在某些生产代码中实际使用它,那么请不要。
话虽如此,要匹配括号,您可以使用balancing groups。从匹配尖括号的文档中调整示例,您会得到:
^[^\(\)]*(((?'Open'\()[^\(\)]*)+((?'Close-Open'\))[^\(\)]*)+)*(?(Open)(?!))$
括号内的内容由上面的表达式[^\(\)]* 定义。我试图将数字和/或规则纳入其中,但没有失去理智并创建超出联邦法规长度的正则表达式,但没有成功。
但是,我们可以结合使用多个正则表达式来验证您的输入。首先,忽略括号并确保表达式是一般形式number (AND/OR number)*:
^[\(\)]*([0-9]|[1-9][0-9]|100)(\s(AND|OR)\s[\(\)]*([0-9]|[1-9][0-9]|100)[\(\)]*)*$
上面的模式已经排除了空括号和带有单个数字的括号。仍然要排除在单个括号内使用多个 AND/OR 的字符串,例如 1 OR 2 AND 3 或 1 OR (2 AND 3 AND 4) 或 1 AND 2 OR (1 AND 2)。
(\d)+\s*(AND|OR)\s*(\d)+\s*(AND|OR)
因此,要验证您的输入,您将运行所有三个正则表达式并断言前两个匹配,而最后一个不匹配。
顺便说一句,您可以使用alternation 作为分支指令,并将这三个正则表达式组合成一个断言与前两个匹配而与最后一个不匹配的正则表达式。这留给读者作为练习,因为这将再次无法阅读。
您可以找到一个有效的 dotnetfiddle 演示 here。以下代码供参考:
using System;
using System.Text.RegularExpressions;
var correctParantheses = new Regex(@"^[^\(\)]*(((?'Open'\()[^\(\)]*)+((?'Close-Open'\))[^\(\)]*)+)*(?(Open)(?!))$");
var correctStructure = new Regex(@"^[\(\)]*([0-9]|[1-9][0-9]|100)(\s(AND|OR)\s[\(\)]*([0-9]|[1-9][0-9]|100)[\(\)]*)*$");
var ambiguousClauses = new Regex(@"(\d)+\s*(AND|OR)\s*(\d)+\s*(AND|OR)");
bool Validate(string input) =>
!string.IsNullOrWhiteSpace(input) &&
correctParantheses.IsMatch(input) &&
correctStructure.IsMatch(input) &&
!ambiguousClauses.IsMatch(input);
var validInputs = new []
{
"1 AND 2",
"(1 AND 2) OR 3",
"1 AND (2 OR 3)",
"(1 AND 2) OR (3 AND 4)",
"1 AND (2 OR (3 AND (4 OR 5)))",
"((((1 AND 2) OR 3) AND 4) OR 5)",
"1"
};
var invalidInputs = new []
{
"(1 AND 2)) OR 3",
"((1 AND 2) OR 3",
"(1)",
"()",
"101",
"1 AND AND 2",
"AND 1 AND",
"AND",
"1 1 AND 2",
"1 OR 2 AND 3",
"1 OR (2 AND 3 AND 4)",
"1 AND 2 OR (1 AND 2)"
};
foreach (var validInput in validInputs)
{
var isValid = Validate(validInput);
if (!isValid)
{
throw new Exception($"Valid input {validInput} did not validate.");
}
}
foreach (var invalidInput in invalidInputs)
{
var isValid = Validate(invalidInput);
if (isValid)
{
throw new Exception($"Invalid input {invalidInput} validated.");
}
}
Console.WriteLine("All cases passed.");
1 直觉上它可以,因为平衡组基本上允许您实现堆栈并使用回溯,应该有一种方法来实现不确定性。但我没有证据或反例。