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