Perl正则表达式不够贪心

Dav*_*ner 3 regex perl regex-greedy

我在perl中编写一个正则表达式来匹配perl代码,该代码启动perl子例程的定义.这是我的正则表达式:

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

$ 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;
}
Run Code Online (Sandbox Code Playgroud)

当我将这个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__
Run Code Online (Sandbox Code Playgroud)

上面的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.
'
Run Code Online (Sandbox Code Playgroud)

Rya*_*son 7

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

你的第三个正则表达式是有效的,因为你无意中将括号中的整个表达式包围起来,以便你可以在它后面添加一个问号.这导致$2一次捕获所有评论,并$3仅捕获最终评论.

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

顺便说一句,看起来你也在子程序名称之后捕获任何空格$1.这是故意的吗?(哎呀,我弄乱了我的助记符,并认为\w"w为空白".)