20

ネガティブな先読み正規表現の詳細を理解するのに苦労しています。正規表現の先読み、後読み、およびアトミックグループを読んだ後、この説明を見つけたとき、私は否定的な先読みの良い要約を持っていると思いました。

(?!REGEX_1)REGEX_2

一致しない場合にのみREGEX_1一致します。チェック後、同じ位置からREGEX_1検索を開始します。REGEX_2

アルゴリズムを理解したいと思って、2文のテスト侮辱を作り上げました。ある単語のない文章を見つけたかったのです。具体的には...

侮辱: 'ヨママは醜いです。そして、彼女は濡れた犬のようなにおいがします。」

要件

  • テスト1:「醜い」なしで文を返します。
  • テスト2:「looks」なしで文を返します。
  • テスト3:「におい」のない文を返します。

テストワードをに割り当て、$arg以前(?:(?![A-Z].*?$arg.*?\.))([A-Z].*?\.)はテストを実装していました。

  • (?![A-Z].*?$arg.*?\.)テストワードを含む文を拒否するための否定的な先読みです
  • ([A-Z].*?\.)少なくとも1つの文に一致します。

重要な部分は、ネガティブ先読みを処理した後、正規表現エンジンがどこでマッチングを開始するかを理解することにあるようです。

期待される結果

  • テスト1($ arg = "醜い"):「そして、彼女は濡れた犬のようなにおいがします。」
  • テスト2($ arg = "looks"):"Yomamaは醜いです。"
  • テスト3($ arg = "smells"):"Yomamaは醜いです。"

実際の結果

  • テスト1($ arg = "醜い"):「そして、彼女は濡れた犬のようなにおいがします。」(成功)
  • テスト2($ arg = "looks"):"Yomamaは醜いです。" (成功)
  • テスト3($ arg = "smells"):失敗、一致なし

([A-Z].*?\.)最初は、貪欲すぎて両方の文に一致したため、テスト3は失敗したと思いました。しかし、(?:(?![A-Z].*?$arg.*?\.))([A-Z][^\.]*?\.)どちらも機能しませんでした。次に、Pythonのネガティブ先読みの実装に問題があるかどうか疑問に思いましたが、perlではまったく同じ結果が得られました。

最後に解決策を見つけました。;を使用して、式の自分の.*?部分のピリオドを拒否する必要がありました。[^\.]*?したがって、この正規表現は機能します。(?:(?![A-Z][^\.]*?$arg[^\.]*?\.))([A-Z][^\.]*?\.)

質問

しかし、私には別の懸念があります。「ヨママは醜いです。」「におい」はありません。それで、.*?欲張りでない一致であると思われる場合、なぜ私はテスト3をで完了できないの(?:(?![A-Z].*?$arg.*?\.))([A-Z].*?\.)ですか?

編集

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

3 に答える 3

3
#!/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);

結果:

neg-lookahead(Yomama) MATCHED: 'And, she smells like an wet dog.'
neg-lookahead(ugly) MATCHED: 'And, she smells like an wet dog.'
neg-lookahead(looks) MATCHED: 'Yomama is ugly.'
neg-lookahead(And) MATCHED: 'Yomama is ugly.'
neg-lookahead(And,) MATCHED: 'Yomama is ugly.'
neg-lookahead(smells) MATCHED: 'Yomama is ugly.'
neg-lookahead(dog) MATCHED: 'Yomama is ugly.'
于 2011-05-25T03:44:56.160 に答える
3

Perl が正規表現を使って何をしているのか興味がある場合は、正規表現デバッガーで実行できます:

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

これにより、熟考するための多くの出力が生成されます。-DDEBUGGING でビルドされた Perl が必要です。

于 2011-05-25T04:55:50.010 に答える
2

あなたの問題は、正規表現エンジンができるだけ一致させようとする(?![A-Z].*?$arg.*?\.)ため、「におい」の場合、文字列全体に一致することになります。(中間のピリオドは、.*?構文の 1 つに含まれます。) 否定的な先読みのケースは、他のケースが可能な限り一致するように制限する必要があります。

それ以外の:

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

使用する:

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

ここで、否定先読みは、最初のピリオドで停止する必要があるため、他の部分よりも多くの文字列に一致することはできません。

于 2011-05-25T04:19:49.250 に答える