2011-05-14 90 views
4

我有一個非貪婪的正則表達式的問題。我已經看到有關於非貪婪的正則表達式的問題,但他們沒有回答我的問題。perl非貪婪問題

問題:我試圖匹配「哈哈」錨的href。

注:我知道這可以用Perl HTML解析模塊來完成,我的問題是約在Perl解析HTML。我的問題是關於正則表達式本身,而HTML僅僅是一個例子。

測試用例:我有4個試驗.*?[^"]。 2首先產生預期的結果。然而,第三不和第四隻是,但我不明白爲什麼。

問題:

  1. 爲什麼沒有第三個測試失敗在這兩個試驗.*?[^"]?不應該不貪心的操作員工作?
  2. 爲什麼第四次測試是否在.*?[^"]的兩次測試中都有效?我不明白爲什麼在前面加入.*會改變正則表達式。 (除了前面的.*之外,第3次和第4次測試是相同的)。

我可能不明白這些正則表達式究竟是如何工作的。 A 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

幽州的問題,並說有產生預期結果的解決方案。我不確定問題是什麼。 – musiKk 2011-05-14 10:00:54

+0

你是對的,我不夠精確。我編輯並更清楚地陳述了這個問題。 – vkats 2011-05-14 10:13:27

回答

6

嘗試打印出$&(整個正則表達式匹配的文本)以及$1。這可以讓你更好地瞭解發生了什麼。

您似乎遇到的問題是,.*?並不意味着「找到所有可能的匹配,在這裏使用最少的字符。」它只是意味着「首先,在這裏嘗試匹配0個字符,並繼續匹配正則表達式的其餘部分,如果失敗,嘗試匹配1個字符,如果正則表達式的其餘部分不匹配,請在這裏嘗試2個字符。 「

Perl將總是找到匹配最接近串的開頭開始。由於您的大部分模式都以href=開頭,因此它會在字符串中找到第一個href=,並查看是否有任何方法可以擴展重複以在此處開始匹配。如果找不到匹配項,則會嘗試從下一個href=開始,依此類推。

當您將貪婪.*添加到正則表達式的開頭時,匹配將從.*開始抓取儘可能多的字符。 Perl然後回溯找到href=。基本上,這會導致它先嚐試最近的href=,並且朝向字符串的開始處工作。

+0

謝謝,似乎是這個問題。它解釋了第一個匹配和回溯井。 – vkats 2011-05-14 10:36:07

+0

要記住的一件好事是貪婪/不貪婪不會改變比賽是成功還是失敗。如果成功貪婪,它會成功不貪婪。如果它失敗貪婪,它將失敗不貪婪。只有在當前位置有多種方式匹配時(從左到右),貪婪纔會發揮作用。在這種情況下,貪婪符合當時可能比賽中最長的比賽,而非貪婪符合當時可能比賽中最短的比賽。 – tadmc 2011-05-14 14:03:09

+0

@cjm:謝謝你,這是我在這個問題上看到的第一個答案,它是關於爲什麼它不起作用以及如何使其工作的實際答案。在有同樣問題的其他問題和答案中,人們只是提供了一種不同的解決方案,而不是真正的答案。 – 2013-04-03 10:26:54

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這樣:/lol/lol/lol/lol/lol

嘗試捕捉所有的.*m~(.*)href="(.*?)"(.*?)>lol~s

$1 contains : 
<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 
$2 contains : 
/lol/lol/lol/lol/lol 
$3 contains : 
class="lol" 

看一看this site它說明了你的正則表達式正在做什麼。

+0

感謝您的回答。你提到**發生了什麼(我已經理解了這一點),但不是**爲什麼會發生。也許我的問題沒有寫得很清楚,所以我編輯了它。 – vkats 2011-05-14 10:15:18

+0

@vkats:我會說,因爲正則表達式這樣工作:-)。它試圖匹配您正在搜索的內容的第一次出現。 – Toto 2011-05-14 10:21:10

+0

我知道它試圖匹配我所說的匹配。顯然,我不明白我說的是什麼,所以我試圖去做。 – vkats 2011-05-14 10:28:05

0

主要的問題是,你不應該使用非貪婪的正則表達式。第二個問題是使用。與*可能意外地匹配更多,你打算。你正在使用的s標誌。甚至更多匹配。

用途:

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

爲你的情況。而關於非貪婪正則表達式,考慮代碼:

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

它不會匹配「xaaac」正如您所料,它會從字符串和匹配「xaaaaab xaaac」的起點開始。貪婪的變體會匹配整個字符串。

問題是,儘管非貪婪的正則表達式並不儘可能多地抓住它們,但它們仍然試圖以與他們貪婪的兄弟一樣的渴望與之匹配。他們會抓住任何一部分弦來做它。

你也可以考慮「佔有」量詞,它關閉回溯。 此外,食譜是好的開始,但如果你想了解事情如何工作,你應該閱讀 - perlre

+0

感謝您的回答(它與另一個給定的幾秒:)前一致)。我忘記了比賽從左邊開始。 – vkats 2011-05-14 10:49:23

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

+0

事實上你的命題'href =「[^」] *「[^>] *> lol' works。Does'href =」[^「] +」[^>] +> lol'(用'+'代替'*')改變含義? – vkats 2011-05-14 11:36:09

+0

@vkats它適合我。由於'href =「」> lol',我使用'*'而不是'+' – gangabass 2011-05-14 11:51:56