2009-10-21 66 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