使用一对Perl正则表达式匹配导致无限循环

6

我编写了一个使用正则表达式的小Perl脚本来获取网站的HTML组件。

我知道这不是一种好的方式来完成这种工作,但我想测试一下我的正则表达式技能。

当使用while循环中的两个正则表达式模式之一运行时,它可以完美地运行并显示正确的输出。但是当我尝试在while循环中检查两个模式时,第二个模式每次都匹配,导致循环无限运行。

我的脚本:

#!/usr/bin/perl -w
use strict;

while (<STDIN>) {

    while ( (m/<span class=\"itempp\">([^<]+)+?<\/span>/g) ||
            (m/<font size=\"-1\">([^<]+)+?<\/font>/g) ) {
        print "$1\n";
    }
}

我正在使用样本输入测试上述脚本:

<a href="http://linkTest">Link title</a>
<span class="itempp">$150</span>
<font size="-1"> (Location)</font>

期望输出:

$150
(Location)

感谢您!非常感谢您的帮助!

如果您正在处理多行,则必须以不同的方式进行处理。由于您逐行处理STDIN,因此内部while仍然无用。使用HTML解析器将是最少出错的解决方案。您还可以尝试使用flipflop运算符..匹配块。 - matthias krull
从这个问题中,我知道这不是做这种工作的好方法,但我想测试一下我的正则表达式技能。 - Borodin
是的,好像没有正则语言问题可以锻炼正则表达式技能一样。每个人都必须尝试在非正则语法上使用正则表达式。现代正则表达式引擎能够处理这些问题,但我几乎总是看到这样的问题,即正则表达式无法处理非正则语言。"但我只想解析HTML的子集..""我想测试我的正则表达式技能""使用解析器太复杂了"..但在一堆错误、失败和边角情况之后,每个人仍然会回到真正的解析器。 - matthias krull
@mugenkenichi,你提出的观点是有道理的。但我做这个小项目的目的不仅仅是让它能够工作。正如你所说,由于HTML是一种非规则语言,因此在实现正则表达式时更有趣(考虑到你只有一个小范围)。如果我正在做一个大项目,我的第一选择显然会是一个好的解析器。但这是我自己的小实验,学习新东西。无论如何,感谢你的意见。我很感激你的帮助。 - javaCity
那很酷。至少这是一个聪明的发泄。 :) - javaCity
显示剩余3条评论
3个回答

9
每当全局正则表达式无法匹配时,它将重置下一个全局正则表达式搜索的位置。因此,当你的两个模式中的第一个匹配失败时,它会强制第二个从字符串的开头重新开始查找。
通过添加“/c”修饰符,可以禁用这种行为,如果正则表达式无法匹配,则保持位置不变。
此外,您可以通过删除转义字符(“"”不需要转义,如果选择其他分隔符,则无需转义“/”)和捕获后多余的“+?”来改进您的模式。
另外,使用use warnings比命令行上的-w更好。
以下是您代码的工作版本。
use strict;
use warnings;

while (<STDIN>) {

    while( m|<span class="itempp">([^<]+)</span>|gc
            or m|<font size="-1">([^<]+)</font>|gc ) {
        print "$1\n";
    }
}

太好了!我对Perl一窍不通。我正在努力学习,因为它非常容易处理所有事情。感谢您清晰简洁地解释答案。 - javaCity
说到这个,我一直试图学习正则表达式,但即使我知道基本的东西,比如 ? 是什么意思,我也不知道如何正确地实现它们。有什么建议吗?谢谢! - javaCity
1
正则表达式.info 是一个有用的资源。当你阅读和尝试时,你会逐渐熟悉它。我建议使用一个正则表达式测试工具来进行实验。这个 支持 Perl 正则表达式。如果你正在运行 Windows,你可能也会喜欢 PCRE Workbench - Borodin

3
while (<DATA>) {
    if (m{<(?:span class="itempp"|font size="-1")>\s*([^<]+)}i) {
        print "$1\n";
    }
}

__DATA__
<a href="http://linkTest">Link title</a>
<span class="itempp">$150</span>
<font size="-1"> (Location)</font>

很抱歉我不得不更改“正确答案”,因为@Borodin提供了一个解释得很好的问题答案。虽然如此,还是非常感谢你的努力。 - javaCity

-3

您在匹配过程中没有更改$_,因此它将始终匹配并进入无限循环。

要解决这个问题,您可以在print之后添加$_=$';,以便在字符串的其余部分再次运行匹配。


2
因为给 $_ 赋值是一个不好的想法。特别是如果它并不是必要的。这种肮脏的技巧应该避免,除非你确切地知道你在做什么以及为什么需要它。 - matthias krull
2
我点踩是因为它是错误的。在标量上下文中进行的全局正则表达式匹配将在上一个成功匹配之后自行搜索,没有任何帮助,这里由于不同的原因而失败了。一般情况下也不应该使用$'变量。正如文档所说,在程序的任何地方使用此变量都会对所有正则表达式匹配产生相当大的性能损失。额外的捕获或substr $_, $+[0]更好。 - Borodin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接