为什么这个递归正则表达式不捕获整个代码块?
Why doesn't this recursive regex capture the entire code block?
我正在尝试编写递归正则表达式来捕获代码块,但由于某种原因,它似乎无法正确捕获它们。我希望下面的代码能够捕获函数的完整主体,但它只捕获第一个 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 ;
}
输出:
{
return x;
}
预期输出:
{
if (x > y)
{
return x;
}
else
{
return y;
}
}
我知道有一个用于此目的的 Text::Balanced
模块,但我正在尝试手动执行此操作以了解有关正则表达式的更多信息。
(?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/
这不会创建额外的捕获。但是,通常不可能编写封装的正则表达式片段。喜欢命名捕获而不是编号捕获等技术可以在此处提供帮助。
您可以将递归模式更改为这个:
/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)
是前一个捕获组的反向引用。
我正在尝试编写递归正则表达式来捕获代码块,但由于某种原因,它似乎无法正确捕获它们。我希望下面的代码能够捕获函数的完整主体,但它只捕获第一个 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 ;
}
输出:
{
return x;
}
预期输出:
{
if (x > y)
{
return x;
}
else
{
return y;
}
}
我知道有一个用于此目的的 Text::Balanced
模块,但我正在尝试手动执行此操作以了解有关正则表达式的更多信息。
(?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/
这不会创建额外的捕获。但是,通常不可能编写封装的正则表达式片段。喜欢命名捕获而不是编号捕获等技术可以在此处提供帮助。
您可以将递归模式更改为这个:
/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)
是前一个捕获组的反向引用。