perl中的无限while循环

时间:2011-09-10 19:33:23

标签: regex perl

有没有办法在没有无限循环的情况下做到这一点?

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

这会导致无限循环,可能是因为直接从while内的正则表达式中分配var会每次都返回“true”?

我知道我可以这样做:

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

但是我希望能省下一条线。是否有我可以使用的正则表达式修饰符或类似的东西?

(另外,如果我想搜索它,这个符号/技巧实际上是什么叫:

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

谢谢!

7 个答案:

答案 0 :(得分:10)

在标量上下文中,带有/g修饰符的正则表达式将充当迭代器,并在没有更多匹配项时返回false值:

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

通过while表达式中的赋值,您将在列表上下文中评估正则表达式。现在你的正则表达式不再像迭代器那样,它只返回匹配列表。如果列表不为空,则计算结果为真值:

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

要解决此问题,您可以从while语句中取出赋值,并使用内置$1变量在循环内进行赋值?

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

答案 1 :(得分:8)

Perl regular expressions tutorial说:

  

在标量上下文中,对字符串的连续调用将使//从匹配跳转到匹配,跟踪字符串中的位置。

可是:

  

在列表上下文中,// g返回匹配分组的列表,如果没有分组,则返回整个正则表达式的匹配列表。

也就是说,在列表上下文中//g一次返回一个包含所有捕获匹配项的数组(之后你将丢弃除第一个之外的所有匹配项),然后每次循环执行时都重复执行(即永远)。

因此,您无法在循环条件中使用列表上下文赋值,因为它不能执行您想要的操作。

如果您坚持使用列表上下文,则可以改为:

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

答案 2 :(得分:8)

  

有没有办法在没有无限循环的情况下做到这一点?

是。使用foreach()而不是while()循环:

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

如果我想搜索它,这个符号/技巧实际上是什么?

在列表上下文中称为匹配。它在“perldoc perlop”中描述:

  

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

答案 3 :(得分:1)

在这种情况下,您无法避免在不改变行为的情况下使用全局变量。

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

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

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

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

答案 4 :(得分:0)

使用较少的代码可以通过多种方式实现此目的。

假设您有一个名为lines.txt的文件:

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

并且您想要提取与正则表达式匹配的部分,即“正则表达式”和“结束”之间的行块。一个简单的Perl脚本是:

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

像这样运行

$ perl match.pl < lines.txt

你得到了

abcdef
ghijk
lmnop
qrstu
vwxyz

你甚至可以在命令行上完成整个事情!

$ perl -nle'print $ 1 if $ _ =〜/ regexline(。+?)end /'&lt; lines.txt ABCDEF ghijk lmnop qrstu VWXYZ

就你的第二个问题而言,我不确定一个特殊的Perl名称。

答案 5 :(得分:0)

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

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

答案 6 :(得分:0)

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

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

for(与foreach相同)将正则表达式匹配扩展为捕获组列表,并打印它们。像这样工作:

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

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

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

除了我们不需要使用转换变量$var之外,它会执行类似原始代码的操作,我们只需立即打印它。