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)は、片方が真で、もう片方が偽となります。
