2011-09-10 85 views
10

有没有办法做到这一点,而没有获得无限循环?perl中的无限while循环

while((my $var) = $string =~ /regexline(.+?)end/g) { 
    print $var; 
} 

这将导致一个无限循环,可能是因为VAR直接从里面同时一个正则表达式的分配返回“true”每一次?

我知道我能做到这一点:

while($string =~ /regexline(.+?)end/g) { 
    my $var = $1;  
    print $var; 
} 

,但我希望我能够挽救一条线。有没有我可以使用的正则表达式修饰符或类似的东西?

(同样,这是什么符号/招居然叫,如果我想寻找它:

(my $var) = $string =~ /regex/; 

谢谢!

回答

8

有没有办法做到这一点没有得到一个无限循环?

是的。使用foreach(),而不是一个while()循环:

foreach my $var ($string =~ /regexline(.+?)end/g) { 

这是什么符号/招居然叫,如果我想寻找它

这就是所谓的匹配列表上下文。它在“perldoc perlop”中有所描述:

g修饰符指定全局模式匹配 - 即在字符串内匹配尽可能多的次数。它的行为方式取决于上下文。在列表环境...

+2

被警告,'foreach'将整个结果集加载到内存中,而不是象'while'一样迭代它。 – hhaamu

10

在标量环境,正则表达式与/g修饰符像一个迭代器,并返回一个错误的值时,有没有更多的匹配:

print "$1\n" while "abacadae" =~ /(a\w)/g;  # produces "ab","ac","ad","ae" 

随着while表达,Y内部分配你正在列表上下文中评估你的正则表达式。现在你的正则表达式不再像迭代器那样工作,它只是返回匹配列表。如果该列表不为空,则计算结果为真值:

print "$1\n" while() = "abacadae" =~ /(a\w)/g; # infinite "ae" 

为了解决这个问题,你可以分配出while语句,并使用内置的$1变量,使循环内的分配?

while ($string =~ /regexline(.+?)end/g) { 
    my $var = $1; 
    print $var; 
} 
0

有几种方法可以用较少的代码来实现。

比方说,你有一个叫lines.txt文件:

regexlineabcdefend 
regexlineghijkend 
regexlinelmnopend 
regexlineqrstuend 
This line does not match 
Neither does this 
regexlinevwxyzend 

,并要提取匹配您的正则表达式的作品,那就是“regexline”和“结束”之间的界线块。一个直接的Perl脚本是:

while (<STDIN>) { 
    print "$1\n" if $_ =~ /regexline(.+?)end/ 
} 

运行时这样

$ perl match.pl < lines.txt 

abcdef 
ghijk 
lmnop 
qrstu 
vwxyz 

你甚至可以做到在命令行整个事情!

$ perl的-Nle '打印$ 1,如果$ _ =〜/regexline(.+?)end/' < lines.txt ABCDEF ghijk lmnop qrstu VWXYZ

至于你的第二个问题去,我不知道这个伎俩的一个特殊的Perl名称。

0

我认为你最好的选择是只更换循环中的$ string ...所以:

while((my $var) = $string =~ /regexline(.+?)end/g) { 
    $string =~ s/$var//; 
    print $var . "\n"; 
} 
+0

我建议你尝试运行该代码;它有语法错误。 'print $ var。 “\ n”;'没问题,但'print'$ var \ n“;'更清洁。你不需要在'$ string'上做另一个替换;你可以捕获初始正则表达式中的新值,并将其赋值给'$ string'。 –

8

Perl regular expressions tutorial说:

在标量环境,对一个字符串连续调用将有//从匹配匹配摹跳,跟踪的位置在字符串中,因为它沿着去。

但是:

在列表环境,// g ^返回匹配的分组列表,或者如果没有分组,整个正则表达式匹配的列表。

也就是说,在//g返回一次所有拍摄比赛的数组(其中您随后丢弃所有,但第一批)名单上下文,然后这是否一遍您的每一次循环的执行(即永远)。

所以你不能在循环条件中使用列表上下文分配,因为它没有做你想做的事。

如果你坚持使用列表范围内,你可以这样做,而不是:

foreach my $var ($string =~ /regexline(.+?)end/g) { 
    print $var; 
} 
0

我不知道你打算用这个打印要做什么,但是这是做的一个很好的方法:

say for $string =~ /regex(.+?)end/g; 

的对(同的foreach)将正则表达式匹配展开成捕获组的列表,并将其打印出来。这样的工作:

@matches = $string =~ /regex(.+?)end/g; 
say for (@matches); 

while有所不同。由于它使用标量上下文,因此它不会将捕获组加载到内存中。

say $1 while $string =~ /regex(.+?)end/g; 

它会像做你的原代码,除了我们并不需要使用转换变量$var,我们只是打印出来的时候了。

1

这是你无法避免使用全局变量而不改变行为的情况。

while ($string =~ /regexline(.+?)end/g) { 
    my $var = $1; 
    ... 
} 

如果您只有一个捕获,您可以通过一次查找所有匹配来避免使用全局变量。

for my $var ($string =~ /regexline(.+?)end/g) { 
    ... 
} 

第二个版本的额外费用通常可以忽略不计。