假设我有一种玩具语言,其中包含以下字符串:
fun( fun3\(\) ) + fun4()
在这里,“ fun”接收到“ fun3()”作为其参数。并且fun4()留待以后评估。
现在说我有一个不同的字符串:
fun( fun3()\\) )
在这里,“ fun”应该收到“ fun3()\”,我们剩下一个)。
通过执行'\'来转义'\'意味着我们从字面上得到它-因此,'\'的/对/不再逃脱括号。第三个\将再次转出括号,依此类推。
现在,假设我想使用C#)功能更强大的Regex库来匹配此字符串,并使用它与方括号匹配的方式,特别是这样;我知道通常我会使用适当的解析方法,而不是(扩展的)正则表达式。这与我应该使用哪种工具无关,而与该工具可以做什么无关。
我将使用以下三个字符串作为测试。
fun(abc) fun3()
这将意味着fun()接收'abc'作为其参数。 fun3()已剩余。
fun(\\\)\)) fun3()
这将意味着fun()接收'\))'作为其参数。 fun3()已剩余。
fun(fun2(\)\\\() ) fun3()
这将意味着fun()接收'fun2()\()'作为其参数。 fun3()已剩余。
正如艾伦·摩尔(Alan Moore)在this StackOverflow question中所假定的那样,我要使用的第一件事是LookBehind。
下面的正则表达式处理第一种情况,但显然不处理第二种情况。看到它的第一个')'太快了。
Regex catchRegex = new Regex(@"^fun\((.*?(?<!\\)(?:\\\\)*)(?<ClosingChar>[\)])(.*$)");
string testcase0 = @"fun(abc) fun3()";
string testcase1 = @"fun(\\\)\)) fun3()";
string testcase2 = @"fun(fun2(\)\\\() ) fun3()";
Console.WriteLine(catchRegex.Match(testcase0).Groups[1]); // 'abc'
Console.WriteLine(catchRegex.Match(testcase0).Groups[2]); // ' fun3()'
Console.WriteLine(catchRegex.Match(testcase0).Groups[3]); // ')'
Console.WriteLine(catchRegex.Match(testcase1).Groups[1]); // '\\\)\)'
Console.WriteLine(catchRegex.Match(testcase1).Groups[2]); // ' fun3()'
Console.WriteLine(catchRegex.Match(testcase1).Groups[3]); // ')'
Console.WriteLine(catchRegex.Match(testcase2).Groups[1]); // 'fun2(\)\\\(' <--!
Console.WriteLine(catchRegex.Match(testcase2).Groups[2]); // ' ) fun3()' <--!
Console.WriteLine(catchRegex.Match(testcase2).Groups[3]); // ')'
因此,现在我们只能做.NET可以做的事情。支架匹配。它通过了第一个测试……但是因为我不告诉它不要在乎逃生的事物,所以它使其他失败。这是公平的。
Regex bracketRegex = new Regex(@"^fun\(([^\)]*|(?<BR>)\(|(?<-BR>)\))(?<ClosingChar>[\)])(.*$)");
Console.WriteLine(bracketRegex.Match(testcase0).Groups[1]); // 'abc'
Console.WriteLine(bracketRegex.Match(testcase0).Groups[2]); // ' fun3()'
Console.WriteLine(bracketRegex.Match(testcase0).Groups[3]); // ''
Console.WriteLine(bracketRegex.Match(testcase1).Groups[1]); // '\\\'
Console.WriteLine(bracketRegex.Match(testcase1).Groups[2]); // '\)) fun3()'
Console.WriteLine(bracketRegex.Match(testcase1).Groups[3]); // ''
Console.WriteLine(bracketRegex.Match(testcase2).Groups[1]); // 'fun2(\' <--!
Console.WriteLine(bracketRegex.Match(testcase2).Groups[2]); // '\\\() ) fun3()' <--!
Console.WriteLine(bracketRegex.Match(testcase2).Groups[3]); // ''
但是问题是下一步。将版本1和版本2结合起来实际上并不能为我带来任何收益。所以对您来说,StackOverflow这个问题是否可以解决?
Regex bracketAwareRegex = new Regex(@"^fun\(([^\)]*|(?<BR>)(?<!\\)(?:\\\\)*\(|(?<-BR>)(?<!\\)(?:\\\\)*\))(?<ClosingChar>[\)])(.*$)");
Console.WriteLine(bracketAwareRegex.Match(testcase0).Groups[1]); // 'abc'
Console.WriteLine(bracketAwareRegex.Match(testcase0).Groups[2]); // ' fun3()'
Console.WriteLine(bracketAwareRegex.Match(testcase0).Groups[3]); // ''
Console.WriteLine(bracketAwareRegex.Match(testcase1).Groups[1]); // '\\\'
Console.WriteLine(bracketAwareRegex.Match(testcase1).Groups[2]); // '\)) fun3()'
Console.WriteLine(bracketAwareRegex.Match(testcase1).Groups[3]); // ''
Console.WriteLine(bracketAwareRegex.Match(testcase2).Groups[1]); // 'fun2(\' <--!
Console.WriteLine(bracketAwareRegex.Match(testcase2).Groups[2]); // '\\\() ) fun3()' <--!
Console.WriteLine(bracketAwareRegex.Match(testcase2).Groups[3]); // ''
因为那没有用。
最佳答案
我提出这个正则表达式:
@"^fun\(((?:[^()\\]|\\.|(?<o>\()|(?<-o>\)))+(?(o)(?!)))\)(.*$)"
ideone demo
我删除了
ClosingChar
捕获。结果:
string testcase0 = @"fun(abc) fun3()";
Console.WriteLine(catchRegex.Match(testcase0).Groups[1]); // 'abc'
Console.WriteLine(catchRegex.Match(testcase0).Groups[2]); // ' fun3()'
string testcase1 = @"fun(\\\)\)) fun3()";
Console.WriteLine(catchRegex.Match(testcase1).Groups[1]); // '\\\)\)'
Console.WriteLine(catchRegex.Match(testcase1).Groups[2]); // ' fun3()'
string testcase2 = @"fun(fun2(\)\\\() ) fun3()";
Console.WriteLine(catchRegex.Match(testcase2).Groups[1]); // 'fun2(\)\\\()'
Console.WriteLine(catchRegex.Match(testcase2).Groups[2]); // ' fun3()'
我还有另一种处理转义字符的方法,该方法使用了一些类似的方法:
(?:[^()\\]|\\.)
与平衡组结合使用时,以上述一种结束。
^fun\( Match 'fun(' literally at the beginning
(
(?:
[^()\\] Match anything not '(', ')' or '\'
|
\\. Match any escaped char
|
(?<o>\() Match a '(' and name it 'o'
|
(?<-o>\)) Match a ')' and remove the named 'o' capture
)+
(?(o)(?!)) Make regex fail if 'o' doesn't exist
)
\)(.*$) Match anything leftover
关于c# - 当方括号可能“转义”时,如何进行正则表达式平衡匹配,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/22745729/