開発ブログ
Git、CSS、HTML、正規表現など、入門者がつまづきそうなポイントを中心に、役立ち情報発信します。。

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

最終更新:2018-03-01 by Joe

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

正規表現「否定先読み」とは?

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

「(?!」と「)」で、サブパターンを囲み、このような記述を用います。

正規表現を含まない記述

「吟味(テスト)」とは、メインの正規表現のマッチングと異なり、もしマッチが見かっても、そのマッチ文字列は「消費(Consume)」されず、続いて起こるメインのマッチング処理の位置に影響しません。また、先読みテストでマッチした文字列は後方参照するできません。「処理の先を呼んで、吟味して、元の位置に戻ってくる」これが先読みという由縁です。このようなテストを「言明(Assertion)」と言います。

このような言明の表現は、否定/肯定の先読み/後読みとして、4つのパターンがあることでよく知られています。

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

否定先読みの使用例

説明だけではだいたいよくわからないので、まずは、否定先読み表現の例を見てみましょう。対象の文字列はこちらを吟味します。

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

基本的な「行頭」を表す位置指定子「^」の直後に、先読みの「元SMAP」が開始するかどうかを吟味します。

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

「.(ドット)」は改行以外の1文字、「*」は、直前のパターンの0回以上の繰り返しを表す量指定子、「$」は行末です。上記の対象文字列は、明らかに「元スマップ」で開始していますので、マッチはしません。

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

※2018/3/1 修正箇所のご連絡ありがとうございました。

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

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

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

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

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

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

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

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

例2-2香取を含む1行

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

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

以上です。

参考

メタ文字の一覧をまとめています。

 

 

閉じる