perl非贪心的问题

时间:2011-05-14 09:02:42

标签: regex perl non-greedy regex-greedy

我遇到了一个非贪婪的正则表达式的问题。我已经看到有关非贪婪的正则表达式的问题,但他们没有回答我的问题。

问题:我正在尝试匹配“lol”锚点的href。

注意:我知道这可以通过perl HTML解析模块完成,我的问题是关于在perl中解析HTML。我的问题是关于正则表达式本身,HTML只是一个例子。

测试用例:我对.*?[^"]进行了4次测试。 2首先产生预期的结果。然而,第3次没有,第4次只是,但我不明白为什么。

问题:

  1. 为什么第三次测试在.*?[^"]的两个测试中都失败了?非贪婪的操作员不应该工作吗?
  2. 为什么第4次测试是否适用于.*?[^"]的两个测试?我不明白为什么在前面加.*会改变正则表达式。 (除了前面的.*之外,第3和第4次测试是相同的。
  3. 我可能不清楚这些正则表达式是如何工作的。 perl cookbook recipe提到了一些内容,但我认为它不能回答我的问题。

    use strict;
    
    my $content=<<EOF;
    <a href="/hoh/hoh/hoh/hoh/hoh" class="hoh">hoh</a>
    <a href="/foo/foo/foo/foo/foo" class="foo">foo </a>
    <a href="/bar/bar/bar/bar/bar" class="bar">bar</a>
    <a href="/lol/lol/lol/lol/lol" class="lol">lol</a>
    <a href="/koo/koo/koo/koo/koo" class="koo">koo</a>
    EOF
    
    print "| $1 | \n\nThat's ok\n" if $content =~ m~href="(.*?)"~s ;
    
    print "\n---------------------------------------------------\n";
    
    print "| $1 | \n\nThat's ok\n" if $content =~ m~href="(.*?)".*>lol~s ;
    
    print "\n---------------------------------------------------\n";
    
    print "| $1 | \n\nWhy does not the 2nd non-greedy '?' work?\n"
      if $content =~ m~href="(.*?)".*?>lol~s ;
    
    print "\n---------------------------------------------------\n";
    
    print "| $1 | \n\nIt now works if I put the '.*' in the front?\n"
      if $content =~ m~.*href="(.*?)".*?>lol~s ;
    
    print "\n###################################################\n";
    print "Let's try now with [^]";
    print "\n###################################################\n\n";
    
    
    print "| $1 | \n\nThat's ok\n" if $content =~ m~href="([^"]+?)"~s ;
    
    print "\n---------------------------------------------------\n";
    
    print "| $1 | \n\nThat's ok.\n" if $content =~ m~href="([^"]+?)".*>lol~s ;
    
    print "\n---------------------------------------------------\n";
    
    print "| $1 | \n\nThe 2nd greedy still doesn't work?\n"
      if $content =~ m~href="([^"]+?)".*?>lol~s ;
    
    print "\n---------------------------------------------------\n";
    
    print "| $1 | \n\nNow with the '.*' in front it does.\n"
      if $content =~ m~.*href="([^"]+?)".*?>lol~s ;
    

4 个答案:

答案 0 :(得分:6)

尝试打印$&(与整个正则表达式匹配的文本)以及$1。这可能会让您更好地了解正在发生的事情。

你似乎遇到的问题是.*?并不意味着“在这里使用最少字符的所有可能匹配中找到匹配。”它只是意味着“首先,尝试在这里匹配0个字符,然后继续匹配其余的正则表达式。如果失败,请尝试匹配1个字符。如果正则表达式的其余部分不匹配,请在此处尝试2个字符。等等。 “

Perl将始终找到开始最接近字符串开头的匹配项。由于大多数模式都以href=开头,因此它会在字符串中找到第一个href=,看看是否有任何方法可以扩展重复以从此处开始匹配。如果无法匹配,则会尝试从下一个href=开始,依此类推。

当你在正则表达式的开头添加一个贪婪的.*时,匹配开始于.*抓取尽可能多的字符。然后Perl回溯找到href=。从本质上讲,这会导致它首先尝试字符串中的 last href=,并朝着字符串的开头工作。

答案 1 :(得分:0)

只有第四个测试用例正在运行。

第一个m~href="(.*?)"~s 这将与字符串中的第一个href匹配,并捕获引号之间的内容,以便:/hoh/hoh/hoh/hoh/hoh

第二个:m~href="(.*?)".*>lol~s 这将匹配字符串中的第一个href并捕获引号之间的内容,然后匹配任意数量的任何字符,直到找到>lol为止:/hoh/hoh/hoh/hoh/hoh

尝试使用.*

捕获m~href="(.*?)"(.*)>lol~s
$1 contains :
/hoh/hoh/hoh/hoh/hoh
$2 contains : 
class="hoh">hoh</a>
<a href="/foo/foo/foo/foo/foo" class="foo">foo </a>
<a href="/bar/bar/bar/bar/bar" class="bar">bar</a>
<a href="/lol/lol/lol/lol/lol" class="lol" 

第三个:m~href="(.*?)".*?>lol~s与上一个测试用例的结果相同。

第四个:m~.*href="(.*?)".*?>lol~s 这将匹配任意数量的任何字符,然后href="然后捕获任何数量的任何非贪婪字符,直到引用,然后匹配任何任何数字的任何字符,直到它找到>lol所以:{{1} }

尝试使用/lol/lol/lol/lol/lol

捕获所有.*
m~(.*)href="(.*?)"(.*?)>lol~s

看一下this site它会解释你的正则表达式在做什么。

答案 2 :(得分:0)

主要问题是你不应该使用非贪婪的正则表达式。第二个问题是使用。与*可能会意外匹配您想要的更多。你正在使用的标志。更加匹配。

使用:

m~href="([^"]+)"[^>]*>lol~

适用于您的情况。关于非贪婪的正则表达式,请考虑以下代码:

$_ = "xaaaaab xaaac xbbc";
m~^x.+?c~;

它与你期望的'xaaac'不匹配,它将从字符串的开头开始并匹配'xaaaaab xaaac'。贪婪的变体会匹配整个字符串。

关键在于,尽管非贪婪的正则表达式并没有尽可能多地抓住它们,但它们仍然试图以某种方式与他们贪婪的兄弟一样渴望。他们会抓住字符串的任何部分来做它。

你也可以考虑“占有”量词,它会关闭回溯。 此外,烹饪书是很好的开始,但如果你想了解事情的确如何运作,你应该阅读这个 - perlre

答案 3 :(得分:0)

让我试着说明这里发生了什么(见其他答案为什么会发生):

href="(.*?)"

匹配:href="/hoh/hoh/hoh/hoh/hoh" 组:/hoh/hoh/hoh/hoh/hoh

href="(.*?)".*>lol

匹配:href="/hoh/hoh/hoh/hoh/hoh" class="hoh">hoh</a> <a href="/foo/foo/foo/foo/foo" class="foo">foo </a> <a href="/bar/bar/bar/bar/bar" class="bar">bar</a> <a href="/lol/lol/lol/lol/lol" class="lol">lol

组:/hoh/hoh/hoh/hoh/hoh

href="([^"]+?)".*?>lol

匹配:href="/hoh/hoh/hoh/hoh/hoh" class="hoh">hoh</a> <a href="/foo/foo/foo/foo/foo" class="foo">foo </a> <a href="/bar/bar/bar/bar/bar" class="bar">bar</a> <a href="/lol/lol/lol/lol/lol" class="lol">lol

组:/hoh/hoh/hoh/hoh/hoh

.*href="(.*?)".*?>lol

匹配:<a href="/hoh/hoh/hoh/hoh/hoh" class="hoh">hoh</a> <a href="/foo/foo/foo/foo/foo" class="foo">foo </a> <a href="/bar/bar/bar/bar/bar" class="bar">bar</a> <a href="/lol/lol/lol/lol/lol" class="lol">lol

组:/lol/lol/lol/lol/lol

编写正则表达式的一种方法是使用:href="[^"]*"[^>]*>lol