Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save think49/04d594a046af08d831b473d9bea654ed to your computer and use it in GitHub Desktop.
Save think49/04d594a046af08d831b473d9bea654ed to your computer and use it in GitHub Desktop.
AND条件を一つの正規表現で実装する方法

[正規表現] AND条件を一つの正規表現で実装する方法

概要

"ABC" かつ "XYZ" を含む文字列にマッチさせる場合において、一つの正規表現で実現する方法を考えます。 なお、"ABC" と "XYZ" のどちらが先に出現するかは決まっていません。

肯定先読み (?=pattern)

肯定先読みで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 を適用する必要があります。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment