使用像perl这样的递归正则表达式匹配Ruby中的平衡括号
我一直在寻找一种方法来匹配正则表达式中的平衡括号,并在Perl中找到了一种使用递归正则表达式的方法:
my $re; $re = qr{ \( (?: (?> [^()]+ ) # Non-parens without backtracking | (??{ $re }) # Group with matching parens )* \) }x;
来自perl正则表达式网站 。
有没有办法在Ruby或类似语言中执行此操作?
更新 :
对于那些感兴趣的人有一些有趣的链接:
Oniguruma手册 – 来自Sawa的回答。
实用程序员的Ruby 1.9正则表达式示例章节
是。 使用oniguruma regex引擎,它可以在Ruby 1.9中内置,并且可以在Ruby 1.8上安装,你可以做到这一点。 您使用(?
或(?'name'...)
。 然后在同一个正则表达式中使用\g
或\g'name'
调用subregex。 所以你的正则表达式转换为oniguruma正则表达式将是:
re = %r{ (? \( (?: (?> [^()]+ ) | \g )* \) ) }x
另请注意,PHP> = 5的多字节字符串模块使用oniguruma regex引擎,因此您将能够执行相同的操作。
oniguruma手册在这里 。
我喜欢上面的解决方案,但经常有人希望忽略转义字符。 假设\转义后续字符,以下正则表达式也处理转义字符。
ESC= /(?[\\](?:[\\][\\])*)/ UNESC= /(?:\A|(?<=[^\\]))(?:[\\][\\])*/ BALANCED_PARENS = /#{UNESC}( (?\( (?> (?> (?:#{ESC}\(|#{ESC}\)|[^()])+ ) |\g )* \)) ) /xm
鉴于负面观察的局限性,由匹配的parens划分的部分将是第一次捕获而不是整个匹配(整个匹配可能包含前导逃逸的反斜杠)。
ESC和UNESC复杂性的原因是假设\\是逃避反斜杠。 我们只在初始paren匹配之前使用UNESC序列,因为任何其他转义括号将在primefaces组内匹配并且永远不会回溯。 实际上,如果我们尝试将UNESC前缀用于内部或最终的匹配,当primefaces组内的[^()]与前导匹配并且拒绝回溯时,它将失败。
这个正则表达式将扫描第一个限定有效平衡括号的paren。 因此,给定字符串“((stuff)”它将匹配“(stuff)”。通常,期望的行为是定位第一个(未转义的)括号并且匹配内部(如果是平衡的)或者不匹配。不幸的是,primefaces分组不会阻止整个正则表达式退出,并且稍后会尝试匹配,因此我们必须在字符串的开头锚定并仅查看第一次捕获。以下正则表达式进行此更改:
BALANCED_PARENS = /\A(?:#{ESC}\(|#{ESC}\)|[^()])*+ (?\( (? (?> (?> (?:#{ESC}\(|#{ESC}\)|[^()])+ ) |\(\g )* \)) ) /xm