【问题标题】:Matching balanced parenthesis in Ruby using recursive regular expressions like perl使用 perl 等递归正则表达式匹配 Ruby 中的平衡括号
【发布时间】:2011-09-13 22:27:19
【问题描述】:

我一直在寻找一种在正则表达式中匹配平衡括号的方法,并在 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

【问题讨论】:

标签: ruby regex perl


【解决方案1】:

是的。使用 oniguruma 正则表达式引擎,它内置在 Ruby 1.9 中,并且可以安装在 Ruby 1.8 上,您可以做到这一点。用(?<name>...)(?'name'...) 命名一个子正则表达式。然后在同一个正则表达式中调用带有\g<name>\g'name' 的子正则表达式。因此,您的正则表达式转换为 oniguruma 正则表达式将是:

re = %r{
  (?<re>
    \(
      (?:
        (?> [^()]+ )
        |
        \g<re>
      )*
    \)
  )
}x

另请注意,PHP >=5 中的多字节字符串模块使用 oniguruma 正则表达式引擎,因此您也可以这样做。

Oniguruma 的手册是here

【讨论】:

【解决方案2】:

我喜欢上述解决方案,但经常希望忽略转义字符。假设 \ 转义了以下字符,则以下正则表达式也处理转义字符。

ESC= /(?<![\\])(?>[\\](?:[\\][\\])*)/
UNESC= /(?:\A|(?<=[^\\]))(?:[\\][\\])*/
BALANCED_PARENS = /#{UNESC}(
                   (?<bal>\(
                    (?>
                      (?>  (?:#{ESC}\(|#{ESC}\)|[^()])+     )
                      |\g<bal>
                    )*
                    \))    ) /xm

鉴于负向后视的限制,由匹配括号分隔的部分将是第一个捕获而不是整个匹配(整个匹配可能包含前导转义的反斜杠)。

ESC 和 UNESC 复杂的原因是假设 \\ 是转义的反斜杠。我们只在初始括号匹配之前使用 UNESC 序列,因为任何其他转义括号都将在原子组内匹配并且永远不会回溯。事实上,如果我们尝试将 UNESC 前缀用于内部或最终括号匹配,那么当原子组内的 [^()] 匹配前导 \ 并拒绝回溯时,它将失败。

这个正则表达式将扫描第一个分隔有效平衡括号的括号。因此,给定字符串“((东西)”,它将匹配“(东西)”。通常,所需的行为是定位第一个(未转义的)括号,或者匹配内部(如果平衡)或不匹配。不幸的是,原子分组不会阻止整个正则表达式退出并在稍后尝试匹配,因此我们必须锚定在字符串的开头并只查看第一次捕获。以下正则表达式进行了此更改:

BALANCED_PARENS = /\A(?:#{ESC}\(|#{ESC}\)|[^()])*+
                  (?<match>\(
                   (?<bal>
                    (?>
                      (?>  (?:#{ESC}\(|#{ESC}\)|[^()])+     )
                      |\(\g<bal>
                    )*
                    \))    ) /xm

【讨论】:

    猜你喜欢
    • 2011-12-19
    • 2010-10-07
    • 1970-01-01
    • 2015-11-04
    • 1970-01-01
    相关资源
    最近更新 更多