使用递归表达式的正则表达式来匹配嵌套大括号?

7

我正在尝试匹配像sp { ...{...}... }这样的文本,其中花括号允许嵌套。到目前为止,我的代码如下:

my $regex = qr/
(                   #save $1
    sp\s+           #start Soar production
    (               #save $2
        \{          #opening brace
        [^{}]*      #anything but braces
        \}          #closing brace  
        | (?1)      #or nested braces
    )+              #0 or more
)
/x;

我就是无法让它与以下文本匹配: sp { { word } }。有人能看出我的正则表达式哪里出了问题吗?
2个回答

6
这是一个很少使用的案例,需要使用非常方便的核心模块Text::Balanced。它确实依赖于发现/设置定界序列开头的pos,所以我通常像这样调用它:
#!/usr/bin/env perl

use strict;
use warnings;

use Text::Balanced 'extract_bracketed';

sub get_bracketed {
  my $str = shift;

  # seek to beginning of bracket
  return undef unless $str =~ /(sp\s+)(?={)/gc;

  # store the prefix
  my $prefix = $1;

  # get everything from the start brace to the matching end brace
  my ($bracketed) = extract_bracketed( $str, '{}');

  # no closing brace found
  return undef unless $bracketed;

  # return the whole match
  return $prefix . $bracketed;
}

my $str = 'sp { { word } }';

print get_bracketed $str;

使用gc修饰符的正则表达式告诉字符串记住匹配的结束点,extract_bracketed使用该信息确定开始位置。

我真的需要研究一下这个模块。它经常出现,但我总是更喜欢正则表达式,因为我已经投入了很多时间学习它,而且学习更多也很有趣,看起来也更紧凑。感谢您的回答! - Nate Glenn
1
@NateGlenn,它确实是正则表达式的补充,特别是正则表达式gc(解析器)功能。这就是为什么它使用字符串的pos,因为预计您将在text_balanced//gc之间交错调用。 - Joel Berger

6

有很多问题。递归部分应该是:

(
   (?: \{ (?-1) \}
   |   [^{}]+
   )*
)

总之:

my $regex = qr/
   sp\s+
   \{
      (
         (?: \{ (?-1) \}
         |   [^{}]++
         )*
      )
   \}
/x;

print "$1\n" if 'sp { { word } }' =~ /($regex)/;

据我所知,正则表达式不允许大括号周围有空格(抱歉押韵了),因此测试用例应该会失败。这是怎么回事? - tripleee
嗯...对于一些部分匹配,比如这个例子:sp {word{(aaaaaaaaaaaaaaaaaaaaaaaaaaaaa)},这个过程会花费很长时间。 - Nate Glenn
@NateGlenn 看看我的解决方案,另外你希望在部分匹配时发生什么,是失败还是希望返回字符串的剩余部分? - Joel Berger
@JoelBerger 不应匹配那个。其余的匹配应该被返回。 - Nate Glenn
1
通过将 [^{}]+ 更改为 [^{}]++ 解决了速度问题。 - ikegami
谢谢!成功或失败就像闪电一样。 - Nate Glenn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接