Regex Perl正则表达式似乎进入了无限循环

Regex Perl正则表达式似乎进入了无限循环,regex,perl,Regex,Perl,我正试图弄明白为什么这段代码不能在某些网站上运行。以下是一个工作版本: …然后这个坏版本,似乎锁定了:完全相同的代码-只是一个不同的URL my $url = "http://www.sport.pl/euro2016/1,136510,20049098,euro-2016-polsat-odkryl-karty-24-mecze-w-kanalach-otwartych.html"; `curl -L '$url' > ./foo.txt`; my $html; open (READ

我正试图弄明白为什么这段代码不能在某些网站上运行。以下是一个工作版本:

…然后这个坏版本,似乎锁定了:完全相同的代码-只是一个不同的URL

my $url = "http://www.sport.pl/euro2016/1,136510,20049098,euro-2016-polsat-odkryl-karty-24-mecze-w-kanalach-otwartych.html";

`curl -L '$url' > ./foo.txt`;

my $html;
open (READPAGE,"<:encoding(UTF-8)","./foo.txt");
    $html = join "\n", <READPAGE>;
close(READPAGE);

# Locks up with this regex. Just seems to be some pages it does it on
my $head;
while( $html =~ m/<head.*?>(.*?)<\/head>/gis ) {
   print qq|FOO: got header...\n|;
}
我搞不清楚这是怎么回事。有什么想法吗

谢谢

更新:对于任何感兴趣的人,我最终离开了用于提取信息的Perl模块,转而使用更健壮的HTML::Parser方法。以下是模块,如果有人想将其用作基础:

 package MetaExtractor;
 use base "HTML::Parser";
 use Data::Dumper;

 sub start {
     my ($self, $tag, $attr, $attrseq, $origtext) = @_;

     if ($tag eq "img") {
         #print Dumper($tag,$attr);

         if ($attr->{src} =~ /\.(jpe?g|png)/i) {
            $attr->{src} =~ s|^//|http://|i; # fix urls like //foo.com
            push @{$Links::COMMON->{images}}, $attr->{src};
         }
     }

     if ($tag =~ /^meta$/i &&  $attr->{'name'} =~ /^description$/i) {
         # set if we find <META NAME="DESCRIPTION"
         $Links::COMMON->{META}->{description} = $attr->{'content'};
     } elsif ($tag =~ /^title$/i && !$Links::COMMON->{META}->{title}) {
         $Links::COMMON->{META}->{title_flag} = 1;
     } elsif ($tag =~ /^meta$/i && $attr->{'property'} =~ /^og:description$/i) {
         $Links::COMMON->{META}->{og_desc} = $attr->{content}
     } elsif ($tag =~ /^meta$/i && $attr->{'property'} =~ /^og:image$/i) {
         $Links::COMMON->{META}->{og_image} = $attr->{content}
     } elsif ($tag =~ /^meta$/i && $attr->{'name'} =~ /^twitter:description$/i) {
         $Links::COMMON->{META}->{tw_desc} = $attr->{content}
     } elsif ($tag =~ /^meta$/i && $attr->{'name'} =~ /^twitter:image:src$/i) {
         $Links::COMMON->{META}->{tw_image} = $attr->{content}
     }
 }

 sub text {
     my ($self, $text) = @_;
     # If we're in <H1>...</H1> or  <TITLE>...</TITLE>, save text
     if ($Links::COMMON->{META}->{title_flag}) { $Links::COMMON->{META}->{title} .= $text; }
 }

 sub end {
     my ($self, $tag, $origtext) = @_;

     #print qq|END TAG: '$tag'\n|;

     # reset appropriate flag if we see </H1> or </TITLE>
     if ($tag =~ /^title$/i) { $Links::COMMON->{META}->{title_flag} = 0; }
 }
它将摘录:

头衔 元描述不是元关键字,但它足够简单使用 FB图像 FB说明 推特图像 Twitter描述 所有的图片都发现它对他们没有任何吸引力。。。i、 有相对URL的网页。。。但如果时间允许,我会玩一玩

只需拨打以下电话:

        my $html;
        open (READPAGE,"<:encoding(UTF-8)","/home/aycrca/public_html/cgi-bin/admin/tmp/$now.txt");

            my $p = new MetaExtractor;
            while (<READPAGE>) {
                $p->parse($_);
            }
            $p->eof;

        close(READPAGE);

这不是一个有限的循环,它只是缓慢的。它也在查找标记,对于每个标记,它必须遍历文件的其余部分,查找不存在的结束标记。将其更改为:

`m/<head\b.*?>(.*?)<\/head>/gis`

如果将非utf8文件视为utf8,问题似乎会更加严重。

您发现了一个灾难性回溯q.v的实例

即使对于那些regex模式适用的站点,匹配也将非常冗长,而且需要大量CPU。你应该避免使用。*?在可能的情况下,使用否定字符类

如果你用这个,一切都会好起来

$html =~ m| <head\b[^<>]*> (.*) </head> |gisx
应该只匹配一个HTML标记,但是没有任何东西可以阻止正则表达式引擎一直搜索到文件的末尾。将此更改为将只允许它与head后面的非尖括号匹配,如果有的话,这将只有几个字符

捕获的表达式不那么简单,因为您可能希望匹配元素中包含的标记,因此否定的字符类将无法工作。然而,灾难性回溯几乎总是多个通配符同时作用的结果,因此一个通配符的每个可能匹配必须与另一个通配符的每个可能匹配匹配,从而导致指数复杂性。只剩下一个通配符,正则表达式应该可以正常工作


还要注意的是,我为正则表达式使用了一个可选的分隔符,这样斜杠就不需要转义了,并且在Ugh之后添加了一个单词boundary\b。不要使用正则表达式来解析像HTML这样的标记语言。它很脏。因此,使用curl而不是LWP获取数据也有点多余。有两种可能性:一种可能的原因是页面中的标签是titanic。另外,因为你有两个非贪婪搜索,你有一个正则表达式,可能需要跳回并多次尝试。另一个可能的原因是此页面包含与正则表达式的第一部分匹配的标记,并且可能导致相同的跳回并重试行为。@Sobrique-这来自Perl模块HTML::Miner。对于从HTML源中提取元标记和图像,您有更好的建议吗?抱歉,无法抗拒…Andrew,查看HTML::Parser谢谢,这就成功了:b在做什么?我以为那是为了边界?是的,这不是一个理想的编码——但这是我在使用perl模块时必须处理的:谢谢\b表示单词边界。意思是在d后面不能是另一个字母。它阻止匹配。啊,非常聪明!从未想过:现在也要玩一玩HTML::Parser。。。只是想看看我能不能找到一个更干净的解决方案。不过,当它允许我的时候,我还是会投赞成票:谢谢你花时间解释它。我已经在ysth的帮助下解决了这个问题,但我也比你投了更高的票。实际上,我现在正在玩HTML::Parser,因为这似乎是一种更快更干净的方法。我只是懒散地使用一个预先存在的Perl模块——但这似乎是一个非常糟糕的选项:@AndrewNewby:HTML::Parser实际上是构建更方便的模块的基类。我建议您查看哪个是基于HTML::Parser构建的,但使用它来构建一个Perl数据结构,您可以随时导航和修改它。。。但我几乎已经用HTML::Parser完成了它。只是适应它的结构所需的时间,等等
$html =~ m| <head\b[^<>]*> (.*) </head> |gisx