JavaScriptには正規表現パターンを作る方法は2つあります。
なんでわざわざ2通りの方法があるかというと用途によって使い分けるため
またどちらを使うかによってもパフォーマンス的な違いが出てきます。
そういう訳でJSでの正規表現パターンの作り方とそれぞれのパフォーマンス的な違いについて紹介
正規表現を表す2通りの方法
JavaScriptで正規表現を顕すには次の2通りの書き方が可能
- 正規表現リテラルを使う
- RegExpオブジェクトを使う
この2つについてそれぞれコード例何かと一緒に説明したいと思います。
正規表現リテラルを使う
これは文字通り正規表現をリテラル(定数)として記述する方法
正規表現リテラルはスクリプトのロード時にコンパイルされます。
その宣言方法は次のようにパターンを // で囲むだけ
1 2 |
/** 正規表現リテラルの例 */ var regexp = /\w+apple/; |
上のコードだと /\w+apple/ が正規表現リテラルになります。
ただしクォート( ' または " )で囲むとただの文字列になってしまうので注意
またリテラルの場合は // の最後にgなどのオプション(フラグ)を付けることも可能です。
1 2 |
/** 正規表現リテラルの例 */ var regexp = /\w+apple/gi; |
ちなみに利用可能なオプションについては次記事でまとめた通り
正規表現オプションの意味と使い方まとめ【JavaScript編】
オプションを使うことで複数行検索などの高度なマッチングができるようになります。
これを正規表現が使える関数に渡すことで検索とか置換ができます。
例えば次はmatch関数に正規表現を渡してテスト文字列を検索しているコード例
1 2 3 4 5 6 7 8 9 |
/** 正規表現リテラル */ var regexp = /\w+apple/gi; /** テスト文字列 */ var testText = 'pineapple'; var foundTexts = testText.match(regexp); if(foundTexts) console.log(foundTexts[0]); /// => pineapple |
以上が正規表現リテラルを使ったパターンの作成方法
こちらはロード時にコンパイルされるので、変数やユーザーの入力から正規表現を作るなどの動的な作成はできません。
RegExpオブジェクトを使う方法
こちらは正規表現をRegExpオブジェクトのコンストラクタ関数を呼び出して作成する方法
正規表現リテラルと違い、RegExpオブジェクトは実行時に動的にコンパイルされます。
使い方はRegExpのコンストラクタ関数に文字列としてパターンを渡すだけ
1 2 |
/** RegExpオブジェクトの例 */ new RegExp('\\w+apple'); |
正規表現リテラルと違い、パターンを // で囲む必要はありません。
また文字列中なのでバックスラッシュ記号( \ )はエスケープする必要がある点にも注意
例えば \w の場合は \\w としなければ正しく動作しません。
またRegExpコンストラクタ関数の第2引数にgとかiとかのフラグが渡せます。
1 2 |
/** RegExpオブジェクトの例 */ new RegExp('\\w+apple', 'gi'); |
こちらも正規表現が使える関数に渡すことで検索や置換が可能です。
例えば次がmatch関数にRegExpオブジェクトを渡してテスト文字列を検索しているコード
1 2 3 4 5 6 7 8 9 |
/** 正規表現リテラル */ var regexp = new RegExp('\\w+apple', 'gi'); /** テスト文字列 */ var testText = 'pineapple'; var foundTexts = testText.match(regexp); if(foundTexts) console.log(foundTexts[0]); /// => pineapple |
以上がRegExpオブジェクトを使った正規表現パターンの作り方
こちらは実行時にコンパイルされるので、外部から受け取ったパターンや変数などを使って動的に正規表現を作ることが可能です。
パフォーマンス的な違い
ここまででも少し触れましたが、正規表現リテラルとRegExpでは次のような違いがあります。
- 正規表現リテラル
スクリプトロード時にコンパイルされる
- RegExpオブジェクト
コンストラクタ関数が実際に呼ばれたときにコンパイルされる
静的に実行されるか動的に実行されるかの違いがあります。
なので当然パフォーマンス的な違いも出てくるはずです。
では実際に本当に違いがあるのか確かめてみました。
その検証に使ったのが次のコード
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var start = performance.now(); for(i = 0; i < 1000000; i++){ /// 正規表現オブジェクトを作成するコード /// つまり次の2つのいずれか /// new RegExp('\\w+apple', 'gi'); /// \w+apple/gi; } var end = performance.now(); var time = (end - start).toFixed(3); console.log(time + 'ms'); |
100万回のループ中で正規表現リテラルまたはRegExオブジェクトを作成
そしてループ前とループ後の時間(ミリ秒)を計測して実行時間を測るという計測方法です。
この方法でそれぞれ10回ずつ試してみた結果がこちら
正規表現リテラル | RegExpオブジェクト | |
---|---|---|
1回目 | 10.1ms | 686.7ms |
2回目 | 10.1ms | 670.4ms |
3回目 | 10.1ms | 670.1ms |
4回目 | 10.1ms | 677.8ms |
5回目 | 9.9ms | 680.2ms |
6回目 | 10.2ms | 680.8ms |
7回目 | 10.1ms | 681.0ms |
8回目 | 10.1ms | 675.4ms |
9回目 | 10.0ms | 670.7ms |
10回目 | 8.1ms | 661.2ms |
平均 | 9.8ms | 675.4ms |
これを見ると歴然ですね。
やはりスクリプトロード時にコンパイルしている正規表現リテラルの方が圧倒的に速いです。
ならいつでも正規表現リテラルを使えばいいのか、というとそうでもありません。
RegExpオブジェクトは動的に実行できるでの次のような場合に役立ちます。
- パターン内で変数を使いたいとき
- 動的にパターンを変化させたいとき
- 外からパターンを受け取りたいとき
正規表現リテラルはロード時にコンパイルされるので、こういうことは不可能です。
なので正規表現パターンを作成するときは
「コード内以外では一切変更しない静的なパターンは正規表現リテラル」
「動的に変化させる必要のあるパターンについてはRegExpオブジェクト」
のように使い分けるのがベストですね。
正規表現のデバッグ・テストに役立つツール
正規表現を使っているとデバッグやテストがしたくなることもあります。
例えば
- どのような内部処理が行われているか知りたい
- 無駄な処理がないかどうかデバッグしたい
- 実際のコードで使う前に正常に動くかテストしたい
など正規表現を最適化したい場合です。
その場合は正規表現ができる regex101 というツールを使うのがおすすめです。
詳しくは次の記事でデバッグ方法について紹介したのでそちらをチェック
内部処理が分かりやすくステップごとに表示されるので、デバッグに役立つはずです。
ここまでのまとめ
JavaScript内での正規表現パターンの作り方についてもう一度まとめ
- 正規表現リテラル
次のように // パターンを囲って定義
1var regexp = /\w+apple/gi;スクリプトロード時にコンパイルされるので一切変更しない静的なパターン作成に使うのがベスト
- RegExpオブジェクトを使う
次のようにRegExpオブジェクトのコンストラクタ関数を呼び出す
1new RegExp('\\w+apple', 'gi');実行時コンパイルされるので動的にパターンを作成したいときに使うのがベスト
動的なパターンが必要ないときはパフォーマンス的になるべく正規表現リテラルを使った方がいいですね。
以上JavaScriptでの正規表現の表し方についてまとめました。
ではでは($・・)/~~~