1. ホーム
  2. regex

[解決済み] 単語を含まない行にマッチする正規表現

2022-03-14 15:08:34

質問

単語をマッチングさせてから、他のツールを使ってマッチングを逆転させることが可能なのは知っています(例. grep -v ). しかし、特定の単語を含まない行にマッチさせることは可能でしょうか、例えば hede 正規表現を使用した場合、どうなりますか?

入力してください。

hoho
hihi
haha
hede

コード

grep "<Regex for 'doesn't contain hede'>" input

希望する出力

hoho
hihi
haha

解決方法は?

regexは逆マッチングをサポートしないという考え方は、完全に正しいとは言えません。負のルックアラウンドを使うことで、この動作を模倣することができます。

^((?!hede).)*$

キャプチャしないバリアント。

^(?:(?!:hede).)*$

上記の正規表現は、任意の文字列、または改行されていない行にマッチします。 ではなく を含む(サブ)文字列 'hede'。前述のように、これは正規表現が得意とする(あるいは得意とすべき)ことではありませんが、それでも、これは 可能です。

また、改行文字もマッチさせる必要がある場合は DOT-ALLモディファイア (末尾の s のようなパターンがある)。

/^((?!hede).)*$/s

またはインラインで使用します。

/(?s)^((?!hede).)*$/

(ここで /.../ は正規表現の区切り文字であり,パターンの一部ではない)

DOT-ALL修飾子が使えない場合は、同じ動作を文字クラスで模倣することができます。 [\s\S] :

/^((?!hede)[\s\S])*$/

説明

文字列は、単に n 文字です。各文字の前後には空の文字列があります。つまり n という文字があります。 n+1 空の文字列 次のような文字列を考えてみましょう。 "ABhedeCD" :

    ┌──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┐
S = │e1│ A │e2│ B │e3│ h │e4│ e │e5│ d │e6│ e │e7│ C │e8│ D │e9│
    └──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┘
    
index    0      1      2      3      4      5      6      7

ここで e は空文字列である。正規表現 (?!hede). が存在しないかどうかを調べます。 "hede" が見えるようにし、そうであれば(だから何か他のものが見えている)、その後に . (ドット)は改行以外の文字にマッチします。ルックアラウンドはまた ゼロ幅アサーション というのも、これらは 消費する 文字がない。何かを主張・検証しているだけなのです。

つまり、この例では、すべての空文字列がまず検証され、その中に "hede" によって文字が消費される前に、その先にある . (ドット)です。正規表現 (?!hede). はそれを一度だけ行うので、グループに包まれて、0回以上繰り返されます。 ((?!hede).)* . 最後に、入力の開始と終了をアンカーで固定し、入力全体が消費されるようにします。 ^((?!hede).)*$

ご覧の通り、入力 "ABhedeCD" は失敗します。 e3 の場合、正規表現 (?!hede) が失敗する(そこに "hede" この先も!)。