regex - Perl 正则表达式不够贪婪

标签 regex perl regex-greedy

我正在用 perl 编写一个正则表达式来匹配开始 perl 子例程定义的 perl 代码。这是我的正则表达式:

my $regex = '\s*sub\s+([a-zA-Z_]\w*)(\s*#.*\n)*\s*\{';

$regex 匹配启动子程序的代码。我还试图在 $1 中捕获子例程的名称以及子例程名称和 $2 中的初始大括号之间的任何空格和注释。 2 美元给我带来了问题。

考虑以下 perl 代码:
my $x = 1;

sub zz
# This is comment 1.
# This is comment 2.
# This is comment 3.
{
    $x = 2;
    return;
}

当我将此 perl 代码放入一个字符串并将其与 $regex 进行匹配时,$2 是“#This is comment 3.\n”,而不是我想要的三行注释。我以为正则表达式会贪婪地将所有三行注释都放入 $2 中,但事实并非如此。

我想了解为什么 $regex 不起作用并设计一个简单的替代品。正如下面的程序所示,我有一个更复杂的替代品 ($re3) 有效。但我认为了解 $regex 为何不起作用对我来说很重要。
use strict;
use English;

my $code_string = <<END_CODE;
my \$x = 1;

sub zz
# This is comment 1.
# This is comment 2.
# This is comment 3.
{
    \$x = 2;
    return;
}
END_CODE

my $re1 = '\s*sub\s+([a-zA-Z_]\w*)(\s*#.*\n)*\s*\{';
my $re2 = '\s*sub\s+([a-zA-Z_]\w*)(\s*#.*\n){0,}\s*\{';
my $re3 = '\s*sub\s+([a-zA-Z_]\w*)((\s*#.*\n)+)?\s*\{';

print "\$code_string is '$code_string'\n";
if  ($code_string =~ /$re1/) {print "For '$re1', \$2 is '$2'\n";}
if  ($code_string =~ /$re2/) {print "For '$re2', \$2 is '$2'\n";}
if  ($code_string =~ /$re3/) {print "For '$re3', \$2 is '$2'\n";}
exit 0;

__END__

上面的 perl 脚本的输出如下:
$code_string is 'my $x = 1;

sub zz
# This is comment 1.
# This is comment 2.
# This is comment 3.
{
    $x = 2;
    return;
} # sub zz
'
For '\s*sub\s+([a-zA-Z_]\w*)(\s*#.*\n)*\s*\{', $2 is '# This is comment 3.
'
For '\s*sub\s+([a-zA-Z_]\w*)(\s*#.*\n){0,}\s*\{', $2 is '# This is comment 3.
'
For '\s*sub\s+([a-zA-Z_]\w*)((\s*#.*\n)+)?\s*\{', $2 is '
# This is comment 1.
# This is comment 2.
# This is comment 3.
'

最佳答案

仅查看捕获 $2 的正则表达式部分.是(\s*#.*\n) .就其本身而言,这只能捕获单个注释行。在它后面有一个星号以捕获多个注释行,这很好用。它捕获多个注释行并将它们每个放入 $2 ,一一,每次替换之前的值$2 .所以$2的最终值当正则表达式完成时,匹配是捕获组匹配的最后一件事,也就是最后的注释行。仅有的。要修复它,您需要将星号放在捕获组中。但是随后您需要放置另一组括号(这次是非捕获)以确保星号适用于整个事情。所以,而不是 (\s*#.*\n)* , 您需要 ((?:\s*#.*\n)*) .

您的第三个正则表达式有效,因为您不知不觉地将整个表达式括在括号中,以便您可以在其后加上问号。这引起了$2一次捕获所有评论,以及 $3仅捕获最终评论。

当您调试正则表达式时,请确保打印出您正在使用的所有匹配变量的值:$1 , $2 , $3等。你会看到 $1只是子程序的名称和 $2只是第三条评论。这可能让您想知道,当第一个和第二个捕获组之间没有任何内容时,您的正则表达式究竟是如何跳过前两个注释的,这最终会引导您发现捕获组多次匹配时会发生什么。

顺便说一句,您似乎还在将子程序名称后面的任何空格捕获到 $1 中。 .这是故意的吗? (糟糕,我弄乱了我的助记符并认为 \w 是“w 表示空格”。)

关于regex - Perl 正则表达式不够贪婪,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9691052/

相关文章:

regex - 如何检索 "_id="和 "&"之间的数据?

Python re.sub() 不会替换所有匹配项

perl - 如何在 Perl 的 Wasm::Wasmtime 中使用二进制 WASM 文件?

perl - 折叠具有多个字段的行

windows - 如何阻止 Perl 程序在完成后关闭其窗口?

java - 另一个不匹配单词的正则表达式帮助

regex - 无法让 Perl 正则表达式成为非贪婪的

java - 简单的 Java 正则表达式不起作用

regex - 使用 perl 替换除最后一次出现的所有内容

javascript - 通过正则表达式用输入替换单词