2009-10-21 37 views
9

說,我有一個包含以下字符串的行:爲什麼我的非貪婪Perl正則表達式仍然匹配太多?

 
"$tom" said blah blah blash. "$dick" said "blah blah blah". "$harry" said blah blah blah. 

,我想提取

 
"$dick" said "blah blah blah" 

我有以下代碼:

my ($term) = /(".+?" said ".+?")/g; 
print $term; 

但它給我超過我需要:

 
"$tom" said blah blah blash. "$dick" said "blah blah blah" 

我試圖通過使用非捕獲括號分組我的圖案作爲一個整體:

my ($term) = /((?:".+?" said ".+?"))/g; 

但問題仍然存在。

我已經重讀了學習Perl的Nongreedy Quantifiers部分,但它到目前爲止讓我無處可尋。

感謝您的任何指導,你可以慷慨地提供:)

+1

第一「等等等等blash」不加引號,所以你的正則表達式被抓住了第二盤。 – Ether 2009-10-25 22:49:43

+0

@其他,我的問題是:我認爲Perl可以將我的模式視爲一個整體。但是我錯了。對我而言,事情變得越來越清楚,即Perl總是試圖匹配第一個子模式,然後再匹配下一個子模式。似乎沒有「同步整體模式匹配」這樣的事情。 – Mike 2009-10-26 02:40:28

+1

@brian,謝謝。我喜歡你改寫我的問題的方式:) – Mike 2009-10-26 02:43:47

回答

18

的問題是,即使它不貪心,它仍然不停地嘗試。正則表達式不看

"$tom" said blah blah blash. 

,並認爲「哦,下面的東西‘說’沒有加引號,所以我會跳過這一個。」它認爲「好吧,」說「之後的內容沒有被引用,因此它仍然是我們報價的一部分。」所以".+?"匹配

"$tom" said blah blah blash. "$dick" 

你需要的是"[^"]+"。這將匹配包含任何不是引號的任何內容的兩個引號。所以,最終的解決方案:

("[^"]+" said "[^"]+") 
+0

感謝克里斯,爲解決方案和解釋:)非常感謝啓蒙! – Mike 2009-10-21 06:05:02

+1

++!使用'[^「] +'的另一個好的理由是它可以減少不必要的反向跟蹤並且使你的正則表達式更有效率 – daotoad 2009-10-21 06:18:18

+1

@daotoad - 我和Alex Martelli在他的回答中討論過這個問題。即使對於10,000,000次比較,在Perl和Python中的性能差異仍然幾乎不明顯,似乎從「編譯器將優化」轉換爲「正則表達式引擎將優化它」:P(請參閱我對他的評論回答我的計時結果。) – 2009-10-21 06:31:40

3

不幸"是需要認真對待的特有的性格不夠。使用:

my ($term) = /("[^"]+?" said "[^"]+?")/g; 

它應該工作正常(它對我來說......!)。即明確地匹配「nondoublequotes」的序列而不是任意字符的序列。

+1

哇! Python之王回答了一個Perl問題!捏我,我在做夢。 (注意,不再需要非貪婪的匹配,並且可能會減緩正則表達式引擎的速度,但是這樣做確實有效)。 – 2009-10-21 06:02:08

+0

我是Perl 4的優秀Perl'er並且這個問題與Perl 4密切相關;-)這是Perl 5的奇怪新穎事物,它讓我失望了,最終成爲了Pythonista! - )(回到「Perl 4」時代,非貪婪永遠不會少效率比貪婪 - 沒有看過任何_current_ Perl RE引擎,看看他們是否也撒嬌了那些, - !)。 – 2009-10-21 06:07:07

+0

Grazie,Alex :) – Mike 2009-10-21 06:14:45

3

其他人提到如何解決這個問題。

我會回答你如何調試這樣的:通過使用更多的捕獲,你可以看到發生了什麼:

bash$ cat story | perl -nle 'my ($term1, $term2, $term3) = /(".+?") (said) (".+?")/g ; 
     print "term1 = \"$term1\" term2 = \"$term2\" term3 = \"$term3\" \n"; ' 
term1 = ""$tom" said blah blah blash. "$dick"" term2 = "said" term3 = ""blah blah blah"" 
2

這裏你的問題是,有你的正則表達式,你想要的(兩種可能的匹配更短的一個)和正則表達式引擎選擇的一個。引擎會選擇特定的匹配項,因爲它更喜歡匹配項,該匹配項在字符串中較早開始匹配,並且對於稍後開始並且較短的匹配項較長。換句話說,早期的比賽贏得較短的比賽。

爲了解決這個問題,你需要讓你的正則表達式更加具體化(就像告訴引擎$ term不應該包含任何引號一樣)。不管怎樣,儘可能使你的正則表達式儘可能具體。

有關詳細信息和陷阱有關正則表達式,我建議傑弗裏Friedl的優秀圖書:Mastering Regular Expressions

+0

@kixx,感謝您的解釋和書籍推薦。 – Mike 2009-10-26 00:44:25