JavaScriptの正規表現の最適化・パフォーマンス改善ポイント
先生と生徒の会話形式で理解しよう
生徒
「先生、正規表現を使って文字列検索すると処理が遅くなることがあります。改善する方法はありますか?」
先生
「もちろんです。正規表現は便利ですが、書き方次第で処理速度に差が出ます。今日はパフォーマンスを上げるポイントを順に解説します。」
生徒
「どのような書き方が遅くなるんですか?」
先生
「特にアンチパターンや無駄な繰り返しがある場合に遅くなることがあります。基本から見ていきましょう。」
1. 正規表現の事前コンパイル
同じ正規表現を何度も使う場合、毎回文字列から正規表現を生成すると無駄な処理が発生します。/pattern/ のように定数として定義して再利用すると効率的です。
const regex = /abc/; // 事前にコンパイル
console.log(regex.test("abcdef")); // 何度でも高速に処理可能
2. 不要な後方参照やグループ化を避ける
正規表現で括弧 () を使うとキャプチャされます。マッチだけが必要でキャプチャは不要な場合は非捕捉グループ (?:...) を使うことで処理が速くなります。
// 通常のグループ(キャプチャされる)
const regex1 = /(abc)/;
regex1.test("abc");
// 非捕捉グループ(キャプチャなし)
const regex2 = /(?:abc)/;
regex2.test("abc");
3. 量指定子の最適化
量指定子 * や + は貪欲マッチをします。特に長い文字列や複雑なパターンではバックトラッキングが増えて処理が遅くなることがあります。必要以上に広い範囲を指定しないことが大切です。
// 遅くなる例(全体を無駄に探索)
const regex1 = /a.*b/;
// 適切に限定すると高速
const regex2 = /a[^b]*b/; // aからbまでの文字だけを対象
4. アンカーを使って検索範囲を限定
正規表現の先頭 ^ や末尾 $ を使うと、検索範囲が限定されるため高速化につながります。
const str = "Hello, World!";
// 先頭からのみ検索
const regex1 = /^Hello/;
console.log(regex1.test(str)); // true
// 末尾のみ検索
const regex2 = /World!$/;
console.log(regex2.test(str)); // true
5. シンプルな文字クラスを使う
複雑なパターンよりも、可能な限り文字クラスや範囲指定でシンプルに書くと処理が速くなります。長い文字列で複雑な OR パターンを使うとバックトラッキングが増えます。
// 遅くなる例
const regex1 = /(cat|dog|mouse|elephant)/;
// シンプルに範囲を限定
const regex2 = /[a-z]+/;
6. 正規表現をキャッシュする
ユーザー入力を使った動的な正規表現を頻繁に生成する場合、同じパターンを使うときはキャッシュして再利用することで高速化できます。
const cache = {};
function getRegex(pattern) {
if (!cache[pattern]) {
cache[pattern] = new RegExp(pattern);
}
return cache[pattern];
}
const regex = getRegex("abc");
console.log(regex.test("abcdef")); // true
7. 最適化のまとめポイント
- 同じ正規表現は事前に定義して再利用する
- 不要なキャプチャグループは非捕捉グループで代用
- 量指定子や OR パターンは必要最小限にする
- 検索範囲をアンカーで限定する
- 文字クラスをシンプルにする
- 動的生成する正規表現はキャッシュして再利用する
正規表現は非常に便利ですが、無駄なバックトラッキングや複雑なパターンは処理を遅くします。シンプルかつ再利用可能な形にすることで、JavaScriptアプリケーションのパフォーマンスを改善できます。