假设我有一种玩具语言,其中包含以下字符串:

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/

10-09 02:37