我遇到了一个非贪婪的正则表达式的问题。我已经看到有关非贪婪的正则表达式的问题,但他们没有回答我的问题。
问题:我正在尝试匹配“lol”锚点的href。
注意:我知道这可以通过perl HTML解析模块完成,我的问题是不关于在perl中解析HTML。我的问题是关于正则表达式本身,HTML只是一个例子。
测试用例:我对.*?
和[^"]
进行了4次测试。 2首先产生预期的结果。然而,第3次没有,第4次只是,但我不明白为什么。
问题:
.*?
和[^"]
的两个测试中都失败了?非贪婪的操作员不应该工作吗?.*?
和[^"]
的两个测试?我不明白为什么在前面加.*
会改变正则表达式。 (除了前面的.*
之外,第3和第4次测试是相同的。我可能不清楚这些正则表达式是如何工作的。 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 ;
答案 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