正規表現で、論理積「AND」の条件、すなわち「かつ」を表す表現方法についてまとめました。
目次
正規表現の「AND(かつ)」の表現
正規表現において、複数のパターンの論理積「AND」、すなわち「A、かつB」といったパターンの並列表現はややトリッキーです。初学者にはややハードルが高いかもしれませんが、記事後半に解説を加えておきましたので、よくわからない方はご一読下さい。
〇〇を含む、AND(かつ)、〇〇を含む
AND(かつ)条件の例として、リテラル(=ただの文字)のマッチを考えてみます。下記は、「ラーメンを含む、かつ、カレーを含む」という、お子様も大好きなメニューの定番をマッチングする正規表現です。
// ラーメンを含み、かつ、カレーを含む文字列の表現
^(?=.*ラーメン)(?=.*カレー).*$
注目すべきは、「(?=」「)」の括弧です。これは肯定先読みを言いますが、こちらについては、追って解説します。
〇〇を含む、AND(かつ)、〇〇を含むまない
同様に、否定先読みを組み合わせて、このように表現できます。
// ラーメンを含み、かつ、パスタを含まない文字列の表現
^(?=.*ラーメン)(?!.*パスタ).*$
『あ〜、「肯定先読み・否定先読み」ね、おっけ〜」となった方は、これ以上の必要は特に必要ないかと思いますが、この記事では、この「肯定先読み」ついて、少し掘り下げていきます。
【補足】肯定先読みについて
肯定先読みの振る舞いはシンプルとは言い難く、やや理解しにくいところがあります。一方で、「AND(かつ)」の正規表現には必要不可欠な物となっています。
「肯定先読み」とは?
正規表現において「肯定先読み」の表現とは下記のような振る舞いです:
- 処理位置の前方方向へ向かって文字列の吟味(言明(Assertion)と言います)を行う。
- マッチングがあれば、そのまま処理位置に戻って処理を再開する。
なんだか、ピンときませんね・・。まずシンプルな例で、肯定先読みの振る舞いを確認しましょう。
「〜で始まる文字列」を考える
下記は「みそ」で始まる文字列の正規表現です。(文字列の範囲を、^と$で囲み「1行」としています。)
// みそで始まる文字列の正規表現
^(?=みそ).*$
// マッチする文字列
みそラーメン
みそスープ?
処理の順番を追っていくと、
- 位置指定子の「^」が「文頭」をマッチ。
- その後「みそ」の吟味を開始。すなわち、文頭の直後に「みそ」がマッチするかをチェック。
- 「みそ」がマッチすれば、文頭の直後に処理位置を戻す。
- 「みそ」の直後から、「.*」のマッチングを開始
いかがでしょうか、なんとなくイメージできたでしょうか・・。
「〜を含む」の正規表現
ちなみに、上記の正規表現の例は、そもそも肯定先読みを使わず「^みそ.*$」でも表現出来てしまいます。しかし、次の「みそを含む文字列」例はどうでしょうか?
// みそを含む文字列の正規表現
^(?=.*みそ).*$
// マッチする文字列
みそラーメン
サッポロ一番みそラーメン
手前みそ
これは「^.*みそ.*$」ではマッチできませんね。行頭の「.*」が、先にすべての文字列をマッチしてしまうからです。上記の例では、肯定先読みの特徴である「マッチングのテスト後、処理位置を戻す」振る舞いのおかげで、「みそ」をマッチしたあと、その後の「.*」が、文頭から文章全体をマッチングしてくれます。
これで「みそ」を含む文字列(部分一致)のパターンが表現できました。
改めて、「AND(かつ)」の正規表現
あとは、上記の応用となります。
// みそを含み、かつ、しょうゆ、かつ、しおを含む文字列の正規表現
^(?=.*みそ)(?=.*しょうゆ)(?=.*しお).*$
// マッチする文字列
みそとしょうゆベースのしおラーメン
しょうゆでも、みそでも構わないけど、しおも良い。
先読みのポイントは「処理が戻る」という点です。これにより、パターンを並列に記述でき、〜を含むについて、「AND(かつ)」を表せるようになりました。
正規表現のAND(かつ)に関する参考情報
今回話題に上がった、否定先読み、肯定先読みについて、こちらにも詳しくまとめています。
また、幾分話はシンプルですが、「OR(または)」の正規表現について、こちらにまとめています。