表現が複雑な場合、その表現が意味をなす理由を説明してください。
3 に答える
実際に を使用している場合はgrep
、オプションを使用して、-v
一致しない行のみを選択できます。
grep -v \(cat\|dog\|fish\|^$\)
このパターンは、空の行と、"cat"、"dog"、および "fish" を含む行を選択します。
わかりました、あなたは使用していませんgrep
。http://www.regular-expressions.info/refadv.htmlによると、正規表現エンジンがサポートしている場合は、次のものが必要です?!
。
`(?!regex)` ゼロ幅の否定先読み。先読み内の正規表現が一致しない場合にのみ全体的な一致が成功することを除いて、正の先読みと同じです。`t(?!s)` は `streets` の最初の `t` に一致します。
特定のフレーズを除外するパターンを構築する方法を探ってみましょう。
任意の文字 (ドットを使用)、ゼロ回以上 (スター.*
)に一致する単純な から始めます。このパターンは、空の文字列1を含む任意の文字列に一致します。
ただし、一致させたくない特定のフレーズがあるため、否定的なルックアラウンドを使用して、不要なものと一致しないようにすることができます。ルックアラウンドはゼロ幅のアサーションです。これは、正規表現エンジンが一致するためにアサーションを満たす必要があることを意味しますが、アサーションは文字を消費しません (つまり、ストリング)。この特定のケースでは、先読みを使用します。これは、アサーションと一致するように現在の位置を先読みするように正規表現エンジンに指示します (もちろん、現在位置の後ろを見る先読みもあります) 。では、やってみ(?!cat|dog|fish).*
ます。
しかし、このパターンを に対して試すとcatdogfish
、一致しatdogfish
ます! 何が起きてる?エンジンが でパターンを使用しようとしたときに何が起こるかを見てみましょうcatdogfish
。
エンジンは、文字列の最初の文字の前から左から右に動作します。最初の試行では、先読みはそのポイントからの次の文字がcat
、dog
、またはfish
ではないことをアサートしますが、実際にはcat
であるため、エンジンはこのポイントから一致できず、2 番目の文字の前に進みます。ここで、アサーションは成功します。これは、後続の次の文字がアサーションを満たさないためです (atf
一致しないcat
または一致dog
しatfi
ないfish
)。アサーションが成功したので、エンジンは を照合でき.*
ます。デフォルトでは、正規表現は貪欲であるため (つまり、可能な限り多くの文字列をキャプチャすることを意味します)、ドット スターは残りの文字列を消費します。
最初のアサーションが成功した後、なぜルックアラウンドが再度チェックされないのか不思議に思うかもしれません。これは、ドット スターが 1 つのトークンとして扱われ、ルックアラウンドが全体として機能するためです。これを変更して、ルックアラウンドが繰り返しごとに 1 回アサートするようにしましょう: (?:(?!cat|dog|fish).)*
.
を非キャプチャグループ(?:…)
と呼びます。一般に、正規表現内のものは括弧でグループ化されますが、これらの括弧はをキャプチャしています。つまり、コンテンツは後方参照(またはサブマッチ)に保存されます。ここではサブマッチは必要ないため、非キャプチャ グループを使用できます。これは通常のグループと同じように機能しますが、後方参照を追跡するオーバーヘッドはありません。
に対して新しいパターンを実行すると、catdogfish
3 つの一致2 :at
と!が得られます。今度は正規表現エンジンの内部で何が起こっているかを見てみましょう。og
ish
ここでも、最初の文字の前にエンジンが始動します。繰り返されるグループに入り((?!cat|dog|fish).
)、アサーションが失敗したことを確認して、次の位置に移動します(a
)。アサーションは成功し、エンジンは に進みt
ます。再びアサーションが成功し、エンジンは再び前進します。この時点で、アサーションは失敗し (次の 3 文字がであるため)、パターンに一致する最大の文字列であるため (これまでのところ、エンジンは左から右に動作しますdog
)、エンジンは一致として返します。at
次に、すでに一致していますが、エンジンは続行します。次の文字に移動し ( o
)、パターンに一致する 2 つの文字を再びピックアップします ( og
)。ish
最後に、文字列の末尾にあるについても同じことが起こります。エンジンが文字列の最後に達すると、それ以上何もすることがなく、ピックアップした 3 つの一致を返します。
このパターンは、許可されていないフレーズを含む文字列の一部と一致するため、まだ完全ではありません。これを防ぐために、パターンにアンカーを導入する必要があります。^(?:(?!cat|dog|fish).)*$
アンカーはゼロ幅アサーションでもあり、エンジンの位置が文字列内の特定の場所でなければならないことをアサートします。この場合、^
文字列の先頭と$
一致し、文字列の末尾と一致します。パターンを に対して照合するとcatdogfish
、これらの小さな一致はいずれもアンカー位置と一致しないため、もはや検出できません。
したがって、最終的な式は になります^(?:(?!cat|dog|fish).)*$
。
1ただし、正規表現で/s
(または「単一行」)修飾子が有効になっていない限り、デフォルトではドットは改行文字と一致しません。
2ここでは、パターンが「グローバル」モードで動作していると仮定しています。これにより、パターンが可能な限り多く一致します。グローバル モードを使用しない場合、パターンは最初の一致のみを返しat
ます。
通常、否定は、grep の -v スイッチや perl の !~ など、正規表現の「周り」のコードに残す方がよいでしょう。解決しようとしている特定の問題はありますか、それとも単なる練習問題ですか?