为什么这个递归正则表达式不捕获整个代码块?



我正在尝试编写递归正则表达式以捕获代码块,但是由于某种原因,它似乎无法正确捕获它们。我希望下面的代码能够捕获功能的完整主体,而是仅捕获第一个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/ints.+?($regex)/s){
    print $1;
}

输出:

{
        return x;
    }

预期输出:

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

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

(?R)递归到整个模式中 - 但是整个模式是什么?当您将引用的$regex嵌入/ints.+?($regex)/中时,将重新编译该模式,(?R)是指新模式。那不是您想要的。

我建议您改用命名捕获,以便您可以按名称重复。更改$regex

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

如果要避免额外的捕获,则可以使用(?(DEFINE) ...)语法来声明名为Regex模式,以稍后调用:

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

然后:/ints.+?((?&nestedbrace))$define_nestedbrace_re/

不会创建其他捕获。但是,通常不可能编写封装的正则片段。诸如首选命名捕获而不是编号捕获的技术可以在这里有所帮助。

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

/ints+.*?  (
    {              # 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)是以前的捕获组的反向引用。

更新的正则示范

最新更新