k8o

文字列に潜む特殊文字を構文として解釈されないように置き換えるRegExp.escape

RegExpオブジェクトを生成するときに正規表現のテキストをそのままの文字として扱いたい時があります。これまでは手動でエスケープする必要がありましたが、RegExp.escape()を使うことで構文として解釈されないような文字列に変換し、文字通りの並びとして検索できます。RegExp.escapeを使ってエスケープ漏れとは縁のない生活を送りましょう。

公開: 2025年5月15日(木)
更新: 2025年5月15日(木)
閲覧数52 views

RegExp.escape()

RegExpオブジェクトを作る時は、new RegExp(str)のように正規表現のテキストstrを渡します。 正規表現のテキストをそのままの文字として扱いたい場合、new RegExp(str)のままではうまく動かないことがあります。

例えば、正規表現のテキストの中に特殊文字が含まれていると、それらが構文として解釈されてしまい、意図した通りに文字列がマッチしないです。

const str = 'abc*';
const regexp = new RegExp(str);
console.log(regexp.test('abc')); // true
console.log(regexp.test('abc*')); // true
console.log(regexp.test('ab')); // true

上記は特殊文字*を含む文字列abc*を正規表現のテキストとして渡しています。 *は正規表現の構文であり、直前の文字が0回以上繰り返されることを意味します。そのため、上記の例ではabc*だけではなく、abcabにもマッチしてしまいます。

abc*という文字列のままマッチさせたい時は、RegExp.escape()を使うことで構文として解釈されないように特定の文字列に変換し、文字通りの並びとして検索することができます。

const str = 'abc*';
// \\x61bc\\*
const escapedStr = RegExp.escape(str);
const regexp = new RegExp(escapedStr);
console.log(regexp.test('abc')); // false
console.log(regexp.test('abc*')); // true
console.log(regexp.test('ab')); // false

abc*という文字列がRegExp.escape()によって\\x61bc\\*に変換され、ababcにはマッチしなくなりました。

RegExp.escape()は手動では達成できない複数のパターンを考慮して実装されています。 単純に正規表現の構文の前に\を追加するだけでは対処できないケースをカバーしてくれるので、古いブラウザのバージョンをサポートしていない限りはRegExp.escape()を使うようにしましょう。

変更の規則

先頭の文字が0~9,a~z,A~Zである場合

先ほどの例で、RegExp.escape('abc*')\\x61bc\\*に変換されました。\\*は想定通りですが、a\\x61に変換されるのは、少し意外だった人もいるのではないでしょうか。

RegExp.escape()は、テキストの先頭の文字が0~9,a~z,A~Zである場合、特殊なエスケープをします。 これらの文字は、\xに続く16進数コード(例: aはASCIIコードが61なので\x61)としてエスケープされます。

これは、別の文字列の後ろに連結して使用する場合に、前の文字列を引き継いだ構文として解釈されないようにするためです。

const str = '0';
const regexp = new RegExp('(.)\\1' + str);
console.log(regexp.test('aa0')); // false
const escapedStr = RegExp.escape(str);
const escapedRegexp = new RegExp('(.)\\1' + escapedStr);
console.log(escapedRegexp.test('aa0')); // true

上の例では、(.)\\1の後ろに0を連結しています。0をエスケープしない場合、new RegExp('(.)\\10')となり「後方参照 10」のように解釈されてしまいます。 RegExp.escape()を使うことで、0\x30に変換され、new RegExp('(.)\\1\\x30')となり「後方参照 1」と「0」で分けて解釈してくれるので、意図した通りにaa0がマッチします。

特殊文字

^$\.*+?()[, ]{}|/は、\を前に付けてエスケープされます。

const str = '^$\.*+?()[]{}|/';
// \\^\\$\\.\\*\\+\\?\\(\\)\\[\\]\\{\\}\\|\\/
const escapedStr = RegExp.escape(str);

これは、正規表現の構文として解釈されないようにするためです。

区切り文字,-=<>#&!%:;@~'・`・"\xに続く16進数コードとしてエスケープされます。

const str = ',-=<>#&!%:;@~`" ';
// \\x2c\\x2d\\x3d\\x3c\\x3e\\x23\\x26\\x21\\x25\\x3a\\x3b\\x40\\x7e\\x60\\x22\\x20
const escapedStr = RegExp.escape(str);

\nのような制御文字は、\を前につけてエスケープされます。

const str = '\f\n\r\t\v';
// \\f\\n\\r\\t\\v
const escapedStr = RegExp.escape(str);
その他の文字

タブ、全角スペースなどの空白文字は、\uに続くUTF-16コード単位へエスケープされます。

const str = ' ';
// \\u3000
const escapedStr = RegExp.escape(str);

ab�(文字化け対策のため、全角バックスラッシュで書くとab\uD800)や�ab\uDFFFab)のような孤立サロゲート(文字列に対してisWellFormed()でチェックできます。)も\uに続くUTF-16コード単位へエスケープされます。

const str = 'abab';
// \\x61b\�\\x20\�ab
const escapedStr = RegExp.escape(str);

この記事はどうでしたか?

500文字以内でご記入ください

ブログの購読

k8oのブログを購読する

k8oのブログを購読することで、最新の情報を受け取ることができます。

登録いただいたメールアドレスは、購読のためにのみ使用されます。