消极的前瞻性Regex贪婪(为什么是*?太贪婪了)

2024-09-28 05:23:37 发布

您现在位置:Python中文网/ 问答频道 /正文

我很难理解负的lookahead正则表达式的细节。在阅读了Regex lookahead, lookbehind and atomic groups之后,当我发现下面的描述时,我想我已经对负面表情有了一个很好的总结:

(?!REGEX_1)REGEX_2

Match only if REGEX_1 does not match; after checking REGEX_1, the search for REGEX_2 starts at the same position.

我希望我能理解这个算法,于是想出了一个两个句子的测试侮辱;我想找到没有特定单词的句子。明确地。。。在

Insult: 'Yomama is ugly. And, she smells like a wet dog.'

Requirements:

  • Test 1: Return a sentence without 'ugly'.
  • Test 2: Return a sentence without 'looks'.
  • Test 3: Return a sentence without 'smells'.

我将测试词分配给$arg,并使用(?:(?![A-Z].*?$arg.*?\.))([A-Z].*?\.)来实现测试。在

  • (?![A-Z].*?$arg.*?\.)是一个否定的前瞻,用来拒绝带有测试词的句子
  • ([A-Z].*?\.)至少匹配一个句子。在

关键的部分似乎在于理解regex引擎在处理完否定的lookahead之后从何处开始匹配。在

预期结果

  • Test 1 ($arg = "ugly"): "And, she smells like a wet dog."
  • Test 2 ($arg = "looks"): "Yomama is ugly."
  • Test 3 ($arg = "smells"): "Yomama is ugly."

实际结果

  • Test 1 ($arg = "ugly"): "And, she smells like a wet dog." (Success)
  • Test 2 ($arg = "looks"): "Yomama is ugly." (Success)
  • Test 3 ($arg = "smells"): Failed, no match

一开始我认为测试3失败是因为([A-Z].*?\.)太贪心,两个句子都匹配;然而,(?:(?![A-Z].*?$arg.*?\.))([A-Z][^\.]*?\.)也不起作用。接下来,我想知道python negative lookahead实现是否有问题,但是perl给出了完全相同的结果。在

最后我找到了解决方案,我不得不使用[^\.]*?来拒绝表达式中的.*?部分中的句点;所以这个regex有效:(?:(?![A-Z][^\.]*?$arg[^\.]*?\.))([A-Z][^\.]*?\.)

问题

然而,我还有另外一个担心,“Yomama很丑。”里面没有“气味”。那么,如果.*?应该是一个非贪婪匹配,为什么我不能用(?:(?![A-Z].*?$arg.*?\.))([A-Z].*?\.)完成测试3呢?在

编辑

根据@bvr关于使用-Mre=debug的优秀建议,我将在工作后再考虑这个问题。在这一点上,赛斯的描述显然是准确的。到目前为止,我学到的是,即使我在NLA中加入非贪心的.*?运算符,负数的lookahead表达式也会尽可能匹配。在


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实现

^{pr2}$

输出

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.'

Tags: andtestreisargregexlike句子
3条回答

您的问题是regex引擎将尽可能努力匹配(?![A-Z].*?$arg.*?\.),因此对于“气味”大小写,它最终匹配整个字符串。(中间的句点被包含在.*?构造之一中)您应该限制负的lookahead大小写以尽可能多地匹配另一个case:

而不是:

(?:(?![A-Z].*?$arg.*?\.))([A-Z].*?\.)

使用:

^{pr2}$

现在,负lookahead不能比其他部分匹配更多的字符串,因为它必须在第一个句点处停止。在

如果您想知道Perl在regex中做什么,可以使用regex调试器运行:

perl -Dr -e '"A two. A one." =~ /(?![A-Z][^\.]*(?:two)[^\.]*\.)([A-Z][^\.]+\.)/; print ">$1<\n"'

你要思考的产出。您需要一个用-DDEBUGGING构建的Perl。在

#!/usr/bin/perl

sub test_re {
    $arg    = $_[0];
    $INSULTSTR = $_[1];
    $INSULTSTR =~ /(?:^|\.\s*)(?:(?![^.]*?$arg[^.]*\.))([^.]*\.)/;
    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 an wet dog.';
test_re('Yomama', $INSULT);
test_re('ugly', $INSULT);
test_re('looks', $INSULT);
test_re('And', $INSULT);
test_re('And,', $INSULT);
test_re('smells', $INSULT);
test_re('dog', $INSULT);

结果:

^{pr2}$

相关问题 更多 >

    热门问题