文字列を正規表現オブジェクトにするときの問題
例えば
re = /hoge\s*fuga/i
みたいな正規表現のfuga
の部分に変数を使いたいときはRegExp
を使えばいいのですが、
str = "fuga"
re = new RegExp("hoge\s" + str, "i");
とすると意味が変わってしまいます。これは"hoge\s"
が文字列としての値を返す際にバックスラッシュの処理がなされ、実際には「hoges
」という文字列になっているからです。そのため、
str = "fuga"
re = new RegExp("hoge\\s" + str, "i")
のようにしなければなりません。
正規表現:RegExp('Hello\sworld!')はマッチしない
タグ付けテンプレートリテラルが便利
ES2015(ES6)で、テンプレートリテラルが加わると同時に文字列をエスケープせずそのまま扱うための仕組みも加わりました。
これを使うとなんと、
str = "fuga"
re = new RegExp(String.raw`hoge\s${str}`, "i")
のように書けちゃいます。バックスラッシュが一回でいい上、変数が埋め込めて便利です。
もっと複雑な例をあげてみます。\(^o^)/
にマッチする正規表現の場合、
re = /\\\(\^o\^\)\//
です(なんかキモい)。この口の部分を変数で指定したいとき、従来だと
mouth = "o"
re = new RegExp("\\\\\\(\\^" + mouth + "\\^\\)/")
となりもはや何をやっているかわかりませんが、String.rawを使えば
mouth = "o"
re = new RegExp(String.raw`\\\(\^${mouth}\^\)/`)
と、リテラルを使った表現に近くなります。
おまけ:メタ文字のエスケープ
変数の部分に外部からの入力を使う場合、メタ文字が入っていると正規表現全体の動作に関わってしまいます。これを防ぐために以下の関数を用意します。
function escapeRegExp(string) {
return ("" + string).replace(/[.*+?^=!:${}()|[\]\/\\]/g, "\\$&");
}
これを入力された文字列に適用します。
mouth = prompt("好きな口を入力してください") || "o"
re = new RegExp(String.raw`\\\(\^${escapeRegExp(mouth)}\^\)/`)
テンプレートリテラルも囲めば\(^o^)/
をわかりやすく出来ますが、場合によっては正規表現である意味がなくなります。
mouth = prompt("好きな口を入力してください") || "o"
re = new RegExp(escapeRegExp(String.raw`\(^${mouth}^)/`))