【发布时间】:2011-09-01 10:13:06
【问题描述】:
我无法理解负前瞻正则表达式的细节。在阅读Regex lookahead, lookbehind and atomic groups 之后,当我发现这个描述时,我以为我对负前瞻有一个很好的总结:
(?!REGEX_1)REGEX_2仅当
REGEX_1不匹配时才匹配;检查REGEX_1后,REGEX_2的搜索从同一位置开始。
希望我理解算法,我编造了一个两句测试侮辱;我想找到没有某个词的句子。具体...
侮辱: 'Yomama 很丑。而且,她闻起来像一条湿狗。'
要求:
- 测试 1:返回一个不带“ugly”的句子。
- 测试 2:返回一个不带“looks”的句子。
- 测试 3:返回没有“气味”的句子。
我将测试词分配给$arg,并使用(?:(?![A-Z].*?$arg.*?\.))([A-Z].*?\.)来实现测试。
-
(?![A-Z].*?$arg.*?\.)是否定前瞻来拒绝带有测试词的句子 -
([A-Z].*?\.)匹配至少一句话。
关键部分似乎在于了解正则表达式引擎在处理否定前瞻后开始匹配的位置。
预期结果:
- 测试 1 ($arg = "ugly"):“而且,她闻起来像一条湿狗。”
- 测试 2($arg = “looks”):“Yomama 很丑。”
- 测试 3 ($arg = "smells"):“Yomama 很丑。”
实际结果:
- 测试 1 ($arg = "ugly"):“而且,她闻起来像一条湿狗。” (成功)
- 测试 2 ($arg = "looks"): "Yomama 很丑。" (成功)
- 测试 3 ($arg = "smells"):失败,不匹配
一开始我以为Test 3失败是因为([A-Z].*?\.)太贪心,两个句子都匹配;但是,(?:(?![A-Z].*?$arg.*?\.))([A-Z][^\.]*?\.) 也不起作用。接下来我想知道python的负前瞻实现是否有问题,但是perl给了我完全相同的结果。
最后我找到了解决方案,我不得不使用[^\.]*? 来拒绝表达式的.*? 部分中的句点;所以这个正则表达式有效:(?:(?![A-Z][^\.]*?$arg[^\.]*?\.))([A-Z][^\.]*?\.)
问题
但是,我还有另一个担心; “Yomama很丑。”里面没有“气味”。那么,如果 .*? 应该是非贪婪匹配,为什么我不能用 (?:(?![A-Z].*?$arg.*?\.))([A-Z].*?\.) 完成测试 3?
编辑
鉴于@bvr 提出的使用-Mre=debug 的极好建议,我会在下班后考虑更多。看来赛斯的描述在这一点上是准确的。到目前为止,我了解到的是,即使我在 NLA 中放置了非贪婪的 .*? 运算符,负前瞻表达式也会尽可能匹配。
Python 实现
import re
def test_re(arg, INSULTSTR):
mm = re.search(r'''
(?: # No grouping
(?![A-Z].*?%s.*?\.)) # Negative zero-width
# assertion: arg, followed by a period
([A-Z].*?\.) # Match a capital letter followed by a period
''' % arg, INSULTSTR, re.VERBOSE)
if mm is not None:
print "neg-lookahead(%s) MATCHED: '%s'" % (arg, mm.group(1))
else:
print "Unable to match: neg-lookahead(%s) in '%s'" % (arg, INSULTSTR)
INSULT = 'Yomama is ugly. And, she smells like a wet dog.'
test_re('ugly', INSULT)
test_re('looks', INSULT)
test_re('smells', INSULT)
Perl 实现
#!/usr/bin/perl
sub test_re {
$arg = $_[0];
$INSULTSTR = $_[1];
$INSULTSTR =~ /(?:(?![A-Z].*?$arg.*?\.))([A-Z].*?\.)/;
if ($1) {
print "neg-lookahead($arg) MATCHED: '$1'\n";
} else {
print "Unable to match: neg-lookahead($arg) in '$INSULTSTR'\n";
}
}
$INSULT = 'Yomama is ugly. And, she smells like a wet dog.';
test_re('ugly', $INSULT);
test_re('looks', $INSULT);
test_re('smells', $INSULT);
输出
neg-lookahead(ugly) MATCHED: 'And, she smells like a wet dog.'
neg-lookahead(looks) MATCHED: 'Yomama is ugly.'
Unable to match: neg-lookahead(smells) in 'Yomama is ugly. And, she smells like a wet dog.'
【问题讨论】:
-
其他故障:
test_re('Yomama',$INSULT);和test_re('And',$INSULT); -
@Mike:是的,你得到了匹配,但它们是糟糕的匹配。它正在返回一个包含坏词的句子。
-
关于您的负面预测,
$arg之后的所有内容有什么意义?在我看来,(?![A-Z][^\.]*?$arg)会像遇到$arg时一样失败(失败是这里的预期行为)。但我不知道 Perl 或 Python。 -
@harpo,我在否定前瞻中使用了
(?![A-Z].*?$arg.*?\.),因为我想拒绝一个带有$arg的句子;但是,我想 100% 确定我尽可能避免与第二句话匹配。因此,我明确匹配了$arg之后的第一个句点 -
我想我的意思是,
(?![^\.]*?$arg)应该这样做。
标签: python regex perl regex-lookarounds