11

说,我有一行包含以下字符串:

“$tom”说废话废话。“$dick”说“等等等等”。“$harry”说废话废话。

我想提取

"$dick" 说 "blah blah blah"

我有以下代码:

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

但它给了我比我需要的更多:

“$tom”说废话废话。"$dick" 说 "blah blah blah"

我尝试使用非捕获括号将我的模式作为一个整体进行分组:

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

但问题仍然存在。

我已经重读了 Learning Perl 的 Nongreedy Quantifiers 部分,但到目前为止它让我无处可去。

感谢您慷慨提供的任何指导:)

4

4 回答 4

19

问题是,即使它不贪婪,它仍然在不断尝试。正则表达式看不到

"$tom" said blah blah blash.

并想“哦,“说”后面的东西没有被引用,所以我会跳过那个。它认为“好吧,“说”之后的东西没有被引用,所以它必须仍然是我们引用的一部分。所以".+?"匹配

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

你想要的是"[^"]+". 这将匹配两个引号,其中包含任何不是引号的内容。所以最终的解决方案:

("[^"]+" said "[^"]+")
于 2009-10-21T05:57:21.320 回答
3

不幸"的是,这是一个足够奇特的角色,需要小心对待。采用:

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

它应该可以正常工作(它对我有用......!)。即显式匹配“非双引号”序列而不是任意字符序列。

于 2009-10-21T06:00:16.950 回答
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""
于 2009-10-21T06:01:04.153 回答
2

您的问题是您的正则表达式有两个可能的匹配项,一个是您想要的(一个较短的),另一个是正则表达式引擎选择的。引擎选择该特定匹配是因为它更喜欢在字符串中较早开始且较长的匹配,而不是较晚开始且较短的匹配。换句话说,早期的比赛会战胜较短的比赛。

要解决这个问题,您需要使您的正则表达式更加具体(如告诉引擎 $term 不应包含任何引号。无论如何,让您的正则表达式尽可能具体是个好主意。

有关正则表达式的更多细节和陷阱,我推荐 Jeffrey Friedl 的优秀书籍:Mastering Regular Expressions

于 2009-10-25T21:27:27.063 回答