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