開発ブログ

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

正規表現:文字やパターンを「含まない」否定の表現まとめ

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

特定の文字や、文字列パターンを含まない正規表現です。特に後述の「否定先読み」は、仕様が混乱しやすいため、整理しながらまとめました。

特定の文字を含まない文字列の正規表現

「否定の文字クラス」を利用した表現

正規表現には「指定した文字のうち、いずれかの文字」といったパターンを表現できる「文字クラス」という記法がありました。

さて、このような文字クラスは、[^文字 ] のように、「^(キャレット)」を開始カッコの直後に付加することで「否定の文字クラス(Negative Character Class)」の表現を行うことができます。キャレットがある事で、「カッコ内に指定した文字以外の文字」といった意味をもったパターンに変わるのです。

下記にいくつか使用例をあげます。

例) アルファベットを含まない文字列

文字クラスでは、「-(ハイフン)」を利用して、「[a-z]」と記述すれば、文字コード上の範囲「a から z まで」を指定することができました。これをそのまま否定文字クラスの表現にする事ができます。

同時に量指定子の「+(1文字以上の連続)」を組み合わせると「アルファベット以外で構成された文字の連続」をマッチすることができます。

例) スペースを含まない文字列

正規表現において、空白(スペース)は、エスケープせず、そのまま記載できるのでした。また、下記の例で使用する両指定子「+」は、「直前のパターンの1回以上の連続」を表します。

エスケープシーケンスによる否定表現

バックスラッシュを使った「エスケープシーケンス」の中には「XXX以外の一文字」を表すものがあります。これらは上記の文字クラスで行うような表現(それ以外の文字も)簡素に記述することができます。

例)空白文字を含まない文字列

さて、文字クラスではありませんが、「\s」は特殊文字の1つで、「空白文字(半角スペース、全角スペース、タブ、改行、改ページ)」を表すことができます。

これの反対となるメタ文字が「\S」で、「空白文字以外の一文字」を表現できます。下記は(例2)とよく似ていますが、スペースだけでなく、より多くの種類の空白文字を含まない表現が記述できます。

別の記事で、このようなエスケープシーケンスを含めた特殊文字をまとめて紹介しています。

ぜひご一読下さい:

特定のパターンを含まない文字列の正規表現

上記の文字クラスで表現できたのは「指定文字以外の文字」といった記述で、あくまで一文字単位でしか表現することができませんでした。一方で、実際に正規表現を用いて記述したパターン自体を否定することもできます。これにより、「指定パターンを含まない文字列」といった、より複雑なマッチング表現が可能になります。

「パターンの否定」を利用する正規表現に「否定先読み」「否定戻り読み」があります。そのうち、今回は「否定先読み」を詳しく紹介します。

「否定先読み」とは?

否定先読みは、「(?!」と、「)」で囲んだ中に、特定の正規表現を書き込むことで記述できます。

正規表現を含まない記述

「否定先読み」で注意が必要なのは、このカッコ内に記述されるパターン(サブパターン)は、実際には消費(Consume)されることなく、吟味(テスト)の目的にのみ利用されるという点です。通常の正規表現のマッチングようにあとから参照することはできませんし、またその後のメインのマッチング走査は、サブパターンの直前のマッチ位置から再開されます。(先読みで吟味したので、吟味が終わったら元の位置に戻る、というイメージ)

メインの走査の位置よりも前方を吟味して、カッコ内の別のパターンへのマッチングを吟味しているのが「先読み」の由縁です。このような、メインの走査処理から独立した吟味(テスト)は「Assertion(言明)」と呼ばれます。

別の味方では、「カッコ内のパターンにマッチしない文字(列)がはじめて抽出されれば、その文字(列)の先頭の位置」といった「位置」を表すための表現のようにも捉えられます、

・・と、説明だけでは、非常にわかりにくいですので、具体例を見てみます。

例)特定の文字列で開始しない文字列

下記の1行を対象に、「否定先読み」を使って、「特定の文字列で開始しない文字列」という、否定を含む正規表現を作ってみます。

私はアメリカで育ちました。

捜査対象とする文字列の範囲を明示するため、「^(行頭)」「$(行尾)」で囲みます。これにより1行を単位として走査します。

「^」が行頭を表します。また、「.」は改行以外のあらゆる一文字です。つまり、上記は「直後に『私は』が現れない行頭と、その直後から続く改行以外の文字の1回以上の連続をマッチする正規表現です。回りくどいようですが、すなわち上記の表現は対象の一行をマッチを見つけることができません。

行頭の直後に「私は」が現れるからです。

否定先読みの例

行頭をあらわす「^」の制約を省いてみます。

これは上記と少し異なり、「『私は』で開始していない位置の直後から続く改行以外の文字の1回以上の連続」にマッチします。これは例1-Aと異なり、「行頭の直後」という成約がないため、すなわち「はアメリカで育ちました」をマッチする事になります。

否定先読み。

例)特定の文字列を含まない文字列

さて、例1は「開始位置」に特化した表現でした。特に開始位置を限定せず、特定の範囲に「特定の文字列を含まない」パターンを記述する方法は、もうすこしトリッキーです。

下記のような表現ができます。

両指定子「*」は0回か、1回以上の文字の連続を表します。

上記は読み解くと、「直後に『アメリカ』か、もしくは『・・・・アメリカ』が現れない行頭と、その直後に続く、0回以上連続する改行以外の文字列」をマッチします。

これにより、行内のいずれかの位置に「アメリカ」が含まれていればマッチしない正規表現、すなわち、「アメリカを含まない1行」といった、否定の正規表現が完成します。

否定先読みの例

否定先読みの「表現がマッチしない(はじめての)位置を探す」という振る舞いする事を意識すると、多用な表現が可能になります。

参考:肯定先読みの利用例

ちなみに、「(?=」 と「)」で囲まれる「肯定先読み」を使えば、逆に「アメリカ」を含む1行をマッチできます。ご参考まで。

肯定先読み。アメリカを含む。

参考

否定先読み、肯定先読みに関してはこちらにもまとめています。

文書全体を対応する時の表現です。

 

正規表現、奥が深いですね。以上です。