Regex Perl正则表达式不是';他不够贪婪

Regex Perl正则表达式不是';他不够贪婪,regex,perl,regex-greedy,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

我正在用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是“#这是注释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__

问题是默认情况下,
\n
不是字符串的一部分。正则表达式在
\n
处停止匹配

您需要使用
s
修饰符进行多行匹配:

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

注意正则表达式后面的
s

如果向捕获组添加重复,它将只捕获该组的最终匹配。这就是为什么
$regex
只匹配最后的注释行

下面是我将如何重写您的正则表达式:

my $regex = '\s*sub\s+([a-zA-Z_]\w*)((?:\s*#.*\n)*)\s*\{';
这与您的
$re3
非常相似,除了以下更改:

  • 空白和注释匹配部分现在位于非捕获组中
  • 我将正则表达式的这部分从
    (…)+)?
    更改为
    (…)*)
    ,这是等效的
只查看正则表达式中捕获
$2
的部分。它是
(\s*.*\n)
。它本身只能捕获一行注释。在它后面有一个星号,以便捕获多个注释行,这很好。它捕获多个注释行,并将每个注释行逐个放入
$2
,每次替换之前的
$2
值。因此,当正则表达式完成匹配时,
$2
的最终值是捕获组匹配的最后一项,即最后的注释行。只有要修复它,您需要将星号放在捕获组中。但是,您需要添加另一组括号(这次是非捕获),以确保星号适用于整个事件。因此,您需要
((?:\s*#.*\n)*
)而不是
((?:\s*#.*\n)*)

您的第三个正则表达式之所以有效,是因为您无意中将整个表达式括在括号中,这样您就可以在它后面加一个问号。这导致
$2
一次捕获所有注释,而
$3
仅捕获最终注释

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


顺便说一下,看起来您还将子例程名称后面的任何空格捕获到
$1
。这是故意的吗?(哎呀,我把我的助记符搞砸了,我想
\w
是“w代表空格”。

这是不正确的,
\n
是字符串的一部分,正则表达式会继续匹配,否则OP的表达式都不会匹配。是的,尽管使用
s
和可能的
m
修饰符可以更好地编写这个正则表达式,没有它们的话,它很相配。这不是问题,谢谢。我想你解决了这个问题。事实上,我正在打印$1,$2。。。调试时。我最小化了我在这里发布的测试代码。| | | | |关于$1,正则表达式中与其匹配的部分是“([a-zA-Z]\w*)”,一个字母字符或下划线,后跟零个或多个字母字符、下划线和数字。这些都不符合空白。我已经测试过了,谢谢。我现在看到了。我想做的事情似乎需要额外的括号。另请参见。e、 例如,
$subs=PPI::Document->new(\$code\u string)->find('PPI::Statement::Sub')
my $regex = '\s*sub\s+([a-zA-Z_]\w*)((?:\s*#.*\n)*)\s*\{';