"ABC" かつ "XYZ" を含む文字列にマッチさせる場合において、一つの正規表現で実現する方法を考えます。 なお、"ABC" と "XYZ" のどちらが先に出現するかは決まっていません。
肯定先読みでAND条件の一つ目の条件(ABC)を先読みする事で、2つ目の条件(XYZ)を検索する事が出来ます。
/**
* "ABC" かつ "XYZ" を含む正規表現パターン (インライン版)
*/
/^(?=.*ABC).*XYZ/.test('ABCDE...XYZ'); // true
.
はデフォルトで改行にマッチしない為、前述の正規表現パターンでは改行を乗り越えて、AND条件を指定することが出来ません。
[\s\S]
を使用する- 正規表現フラグ
s
を指定する (.
が全ての一文字にマッチするようになる) ※要 ECMAScript 2018
/**
* "ABC" かつ "XYZ" を含む正規表現パターン (マルチライン版)
*/
/^(?=[\s\S]*ABC)[\s\S]*X/.test('ABCDE\n...XYZ'); // true
/^(?=.*ABC).*XYZ/s.test('ABCDE\n...XYZ'); // true
なお、 正規表現フラグ s
は ECMAScript 2018 (ES9)でリリース予定であり、一部のブラウザで実装されていますが、まだ正式版ではありません。
(2018/04/10現在、TC39 Stage 4なので将来的に全ブラウザで実装されると思われます)
正規表現には次の性質があります。
- 前方にワイルドカードがあると遅い
- 最長一致である
- 最短一致はバックトラックによって検索される
AND条件を一つの正規表現パターンで実装するには、一つ目の条件を先読みし、改めて2つ目の条件を消費させる必要があり、それがワイルドカード(.*
)が前方に存在する理由です。
前方にワイルドカードを指定しない為には、2通りの順番で正規表現をOR指定しなければなりません。
/**
* "ABC" かつ "XYZ" を含む正規表現パターン (インライン版)
*/
/ABC.*XYZ|XYZ.*ABC/
一応、指定できますが、条件が3つ、4つ..と増えていく間に複雑化する為、拡張性が悪い欠点があります。 また、最長一致の問題がこの正規表現ではクリアできません。
/ABC.*XYZ|XYZ.*ABC/.test('ABCDE...XYZ...XYZ...XYZ'); // true
このコードは正しく true
を返しますが、最長一致故に3つ目の "XYZ" まで消費されます。
これを最短一致(.*?
)に変えても、バックトラック分の時間が余計にかかります。
バックトラックを発生させずに最短一致させるには次のように書く必要があります。
/ABC(?:(?!XYZ).)*XYZ|XYZ(?:(?!ABC).)*ABC/.test('ABCDE...XYZ...XYZ...XYZ'); // true
ただ、単にAND条件を指定する為だけにずいぶんと大仰な正規表現になってしまいました。
AND条件は素直に、2回マッチさせるのがパフォーマンス的にも可読性的にもベストと思います。
var string = 'ABCDE\n...XYZ...XYZ...XYZ';
/**
* "ABC" かつ "XYZ" を含む正規表現パターン (マルチライン版)
*/
/ABC/.test(string) && /XYZ/.test(string); // true
/**
* "ABC" かつ "XYZ" を含む非正規表現コード
*/
string.includes('ABC') && string.includes('XYZ'); // true
String.prototype.includes
は IE11- がサポートしていない為、Polyfill を適用する必要があります。