2. 複数の指定文字列を含まない行の検索
次は、「指定文字列を含まない」という条件を組み合わせた、複数の文字列指定による検索の正規表現です。
文字列abを含まない、かつ、 文字列bcを含まない、かつ、 文字列cdを含む行を検索する。
対象データと期待する検索結果は、下記となります(黄緑色が検索にヒットした部分です)。
この問題のポイントは、文字列abを含まない、かつ、文字列bcを含まない、という条件です。
文字aを含まない、かつ、文字bを含まない、かつ、文字列cdを含む行を検索するのであれば、^[^ab]*cd[^ab]*$
でもいいのですが、「文字列を含まない」という条件となっているので、否定先読みを使う必要があります。
答えは、下記となります。
^(?!.*ab)(?!.*bc)(?=.*cd).*$
否定先読みは、指定したパターンが先に存在しない位置にマッチします。たとえば、(?!yz)
は次にyzがない位置にマッチしますので、検索対象が
xyz
であれば、行頭と文字xの間、文字yと文字zの間、文字zと行末の間、の3箇所の位置にマッチします。
行頭から行末までの間に、abがなく、bcもなく、cdがある行にマッチする。と考えてもいいですが、行頭にマッチして、その後.*ab
がない位置にマッチして、.*bc
がない位置にマッチして、.*cd
がある位置にマッチして、.*$
にマッチすると考えてもいいです。
もしくは、行頭、かつ.*ab
がない、かつ、.*bc
がない、かつ、.*cd
がある位置からの.*$
にマッチすると考えてもいいでしょう。言いかえると、肯定先読みと否定先読みを組み合わせて、論理演算を行っているということです。
正規表現のイメージは、下記となります。
肯定先読みと否定先読みは、それぞれ、肯定先読みが存在肯定命題(指定したパターンが存在する)、否定先読みが存在否定命題(指定したパターンが存在しない)と考えると、分かりやすいでしょう。
例えば正規表現A?B{1,2}
は、下記の4通りの文字列にマッチしますが、
B
BB
AB
ABB
(?=A?B{1,2})
は、4通りの少なくとも1つがある位置にマッチし、(?!A?B{1,2})
は、4通りのどの1つも存在しない位置にマッチします。
ちなみに、指定するパターンが同じである、肯定先読みと否定先読みは、余事象であるため、必ず違う真偽値(片方が真で、片方が偽)となります。例えば、(?=.*ab)
と(?!.*ab)
は、片方が真で、もう片方が偽となります。