【发布时间】:2016-04-21 13:16:07
【问题描述】:
我正在尝试匹配句子中的可选(可能存在)短语:
perl -e '$_="word1 word2 word3"; print "1:$1 2:$2 3:$3\n" if m/(word1).*(word2)?.*(word3)/'
输出:
1:word1 2: 3:word3
我知道第一个 '.*' 是贪婪的,并且将所有内容都匹配到 'word3'。让它不贪婪并没有帮助:
perl -e '$_="word1 word2 word3"; print "1:$1 2:$2 3:$3\n" if m/(word1).*?(word2)?.*(word3)/'
输出:
1:word1 2: 3:word3
这里似乎存在利益冲突。我会认为 Perl 会匹配(word2)?如果可能,仍然满足非贪婪的 .*?。至少这是我对“?”的理解。 Perl 正则表达式页面显示“?”进行 1 次或 0 次,所以它不应该更喜欢一次匹配而不是零次吗?
如果我捕获了 .*?:
perl -e '$_="word1 word2 word3"; print "1:$1 2:$2 3:$3 4:$4\n" if m/(word1)(.*?)(word2)?.*(word3)/'
输出:
1:word1 2: 3: 4:word3
这里的所有组都是捕获组所以我不知道为什么它们是空的。
只是为了确保没有捕获单词间空间:
perl -e '$_="word1_word2_word3"; print "1:$1 2:$2 3:$3 4:$4\n" if m/(word1)(.*?)(word2)?.*(word3)/'
输出:
1:word1 2: 3: 4:word3
鉴于唯一未捕获的匹配项是 word2 和 word3 之间的匹配项,我只能假设它是匹配项。 果然:
perl -e '$_="word1_word2_word3"; print "1:$1 2:$2 3:$3 4:$4 5:$5\n" if m/(word1)(.*?)(word2)?(.*)(word3)/'
输出:
1:word1 2: 3: 4:_word2_ 5:word3
所以贪心匹配是倒退的,Perl 很乐意匹配 word2 的零个(而不是一个)实例。让它不贪婪也无济于事。
所以我的问题是:如何编写正则表达式来匹配和捕获句子中可能的短语?我在这里给出的例子很简单。我正在解析的实际句子要长得多,在我匹配的句子之间有很多单词,所以我不能假设任何长度或中间文本的组成。
非常感谢, 斯科特
【问题讨论】:
-
乍一看,您为第一个不分青红皂白的模式
.*选择了非贪婪匹配,然后是一个可选模式((word2)?)和另一个包罗万象的表达式(.*),后者再次匹配贪婪并获取您的输入,这就是为什么您的捕获组 2 (3) 永远不会被填充。 -
不要使用通配符“.*”,而是尝试使用“\s”字符类作为带有“+”量词的空格。像这样的“\s+”。
-
贪婪匹配正在倒退 - 你知道,带有子模式的贪婪量词会导致回溯。真正的答案是:使用带有否定可选模式的缓和贪婪令牌直到可选模式:
(word1)(?:(?!word2).)*(word2)?.*?(word3) -
@WiktorStribiżew 您是否有机会将您的评论转换为答案?我想投票。
-
@Scott:我添加了一个答案。