開発ブログ

WWWクリエイターズが送る、Git、CSS、HTML、コマンドライン、Macの便利機能など、開発に関する役立ち情報発信します。気まぐれに更新。

正規表現:良く分かる、否定先読み・否定後読み。

最終更新:2017-09-22 by Joe

正規表現で、便利だが、分かりにくい「否定先読み」「否定後読み」についてまとめました。

「否定先読み」とは?

正規表現における「否定先読み(Negative lookahead)」とは、メインのマッチング処理とは独立したサブパターンのマッチングの吟味(テスト)の事です。

否定・肯定の先読み、後読みとして、4つのパターンがあることでよく知られています。まずはざっくり一覧です。

名前表現詳細
否定先読み
(Negative lookahead)
(?!pattern)開始位置から前を呼んでpatternがマッチしなければテストはパス
否定後読み
(Negative lookbehind)
(?<!pattern)開始位置から後ろを呼んでpatternがマッチしなければテストはパス
肯定先読み
(Positive lookahead)
(?=pattern)開始位置から前を呼んでpatternがマッチすればテストはパス
肯定後読み
(Positive lookbehind)
(?<=pattern)開始位置から前を呼んでpatternがマッチすればテストはパス

「テスト」とは、メインの正規表現のマッチングと異なり、もしマッチが見かっても、そのマッチ文字列は「消費(Consume)」されず、次のマッチングの吟味の位置に影響しません。

「先を呼んで、吟味して、元の位置に戻ってくる」これが先読みという由縁です。このようなテストを「言明(Assertion)」と言います。

また、先読みテストでマッチした文字列は後方参照するできません。

否定先読みの使用例

説明だけではだいたいよくわからないので、まずは、否定先読み表現の例を見てみましょう。前述の通り、否定先読みは、「(?!」と「)」で、サブパターンを囲みます。

正規表現を含まない記述

対象の文字列はこちらを吟味します。

[例1-1]「元SMAP」で開始しない1行

基本的な「行頭」を表す、位置指定子「^」の直後に、先読みの「ベッキー」が開始するかどうかを吟味します。「.(ドット)」は改行以外の1文字、「*」は、直前のパターンの0回以上の繰り返しを表す量指定子、「$」は行末です。

// 元スマップで開始しない1行
^(?!元スマップ).*$

対象文字列は、明らかに「元スマップ」で開始していますので、マッチはしません。

比較のため「肯定先読み」で考えています。

[例1-2]「元SMAP」で開始する1行

肯定の先読みは、その名の通り、カッコ内のサブパターンがマッチすればテストが成功します。

// 元スマップで開始する1行
^(?!元スマップ).*$

サブパターンのテストが成功しても、マッチ文字列は「消費されない」のでした。

[例2-1]「香取」を含まない1行

さて、前述の例は「行頭」という条件で吟味していましたが、1文全体を対象にしています。

否定先読みを使って、「香取」を含まない1行をマッチングしようとしてみます。

// 香取を含まない1行
^(?!.*香取).*$

[例2-2]香取を含む1行

もう想像が付きそうですが、香取を含むには、肯定先読みで表現できます。

// 香取を含む1行
^(?=.*香取).*$

 

以上です。

参考

 

 

 

閉じる