4

我正在尝试编写一个递归正则表达式来捕获代码块,但由于某种原因,它似乎没有正确捕获它们。我希望下面的代码能够捕获函数的完整主体,但它只捕获第一个if语句的内容。

这几乎就像以.+?某种方式吞噬了第一个{,但它应该是非贪婪的,所以我不明白为什么会这样。

是什么导致它以这种方式行事?

脚本:

use strict;
use warnings;

my $text = << "END";
int max(int x, int y)
{
    if (x > y)
    {
        return x;
    }
    else
    {
        return y;
    }
}
END

# Regular expression to capture balanced "{}" groups
my $regex = qr/
    \{              # Match opening brace
        (?:         # Start non-capturing group
            [^{}]++ #     Match non-brace characters without backtracking
            |       #     or
            (?R)    #     Recursively match the entire expression
        )*          # Match 0 or more times
    \}              # Match closing brace
/x;

# is ".+?" gobbling up the first "{"?
# What would cause it to do this?
if ($text =~ m/int\s.+?($regex)/s){
    print $1;
}

输出:

{
        return x;
    }

预期输出:

{
    if (x > y)
    {
        return x;
    }
    else
    {
        return y;
    }
}

我知道有一个Text::Balanced用于此目的的模块,但我正在尝试手动执行此操作以了解有关正则表达式的更多信息。

4

2 回答 2

6

(?R)递归到整个模式——但整个模式是什么?当您将引用的内容嵌入$regex/int\s.+?($regex)/中时,该模式将被重新编译并(?R)引用新的模式。那不是你想要的。

我建议您改用命名捕获,以便您可以按名称递归。换个$regex

/(?<nestedbrace> ... (?&nestedbrace) ...)/

如果你想避免额外的捕获,你可以使用(?(DEFINE) ...)语法来声明可以稍后调用的命名正则表达式模式:

my $define_nestedbrace_re = qr/(?(DEFINE)
  (?<nestedbrace ... (?&nestedbrace) ...)
)/x;

然后:/int\s.+?((?&nestedbrace))$define_nestedbrace_re/

这不会创建额外的捕获。但是,通常不可能编写封装的正则表达式片段。喜欢命名捕获而不是编号捕获之类的技术可以在这里提供帮助。

于 2017-09-08T14:08:44.703 回答
1

您可以将递归模式更改为此:

/int\s+.*?  (
    \{              # Match opening brace
        (?:         # Start non-capturing group
            [^{}]++ # Match non-brace chars without backtracking
            |       # OR
            (?-1)   # Recursively match the previous group
        )*          # Match 0 or more times
    \}
)/sx
  • 注意使用(?-1)而不是(?R)递归整个匹配的模式。
  • (?-1)是前一个捕获组的反向引用。

更新的 RegEx 演示

于 2017-09-08T14:13:38.027 回答