JavaScriptにRegExp.escape関数はありますか?


442

可能な文字列から正規表現を作成したいだけです。

var usersString = "Hello?!*`~World()[]";
var expression = new RegExp(RegExp.escape(usersString))
var matches = "Hello".match(expression);

そのための組み込みの方法はありますか?そうでない場合、人々は何を使用しますか?Rubyは持っていRegExp.escapeます。私は自分で書く必要があるとは思わない、そこに何か標準があるはずだ。ありがとう!


15
RegExp.escape現在取り組んでいる素晴らしい人々にあなたをアップデートしたかっただけあり、彼らが貴重なインプットを持っていると思っている人は誰でも貢献して大歓迎です。core-jsと他のポリフィルがそれを提供します。
Benjamin Gruenbaum 2015年

5
この回答の最近の更新によると、この提案は拒否されました:問題を参照してください
try-catch-finally

回答:


573

上記にリンクされた機能は不十分です。文字グループで範囲に使用される^or $(文字列の開始と終了)またはをエスケープできません-

この関数を使用します。

function escapeRegex(string) {
    return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}

一見不要と思われるかもしれませんが、エスケープ-(および^)は、文字クラスや正規表現の本文に挿入する文字をエスケープするのに適しています。

エスケープ/は、関数をエスケープして、後で評価するためにJS正規表現リテラルで使用するのに適しています。

それらのいずれかをエスケープすることのマイナス面はないので、より広いユースケースをカバーするためにエスケープすることは理にかなっています。

そして、はい、これが標準のJavaScriptの一部ではないことは残念なことです。


16
実際に、我々はエスケープする必要はありません/すべてで
ソーン

28
@Paul:Perl quotemeta\Q)、Python re.escape、PHP preg_quote、Ruby Regexp.quote...
2013年

13
この関数をループで使用する場合は、RegExpオブジェクトを独自の変数にvar e = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g;して、関数を作成return s.replace(e, '\\$&');することをお勧めします。これにより、RegExpを1回だけインスタンス化できます。
スタイル

15
組み込みオブジェクトの拡張に対する標準的な引数がここに適用されますか?ECMAScriptの将来のバージョンRegExp.escapeが、あなたの実装とは異なる実装を提供する場合はどうなりますか?この関数を何にも付けない方がいいのではないでしょうか?
Mark Amery、2015

15
ボビンスはエスリントの意見を気にしない
ボビンス

113

lodashを使用している人は、v3.0.0以降、_escapeRegExp関数が組み込まれています。

_.escapeRegExp('[lodash](https://lodash.com/)');
// → '\[lodash\]\(https:\/\/lodash\.com\/\)'

また、完全なlodashライブラリを必要としない場合は、その関数だけが必要になることがあります


6
これだけのnpmパッケージさえあります!npmjs.com/package/lodash.escaperegexp
Ted Pennings、2015年

1
これは、そのような単純なことのために実際に存在する必要がないコードのロードをインポートします。ボビンスの答えを使用してください...私にとっては動作し、ロードするバイト数はlodashバージョンよりもはるかに少ないです!
Rob Evans

6
@RobEvans私の答えは「lodashを使用するすべての人のために」で始まり、関数だけが必要であるとさえ言っていますescapeRegExp
gustavohenke 2017

2
@gustavohenkeすみません、もう少し明確にしておかなければなりません。リンクされたモジュールを「ちょうどその関数」に含めました。それが私がコメントしていたものです。見てみると、単一の正規表現が含まれている単一の関数を効果的にするためのコードはかなりたくさんあります。lodashをすでに使用している場合は同意し、使用することは理にかなっていますが、それ以外の場合は他の回答を使用してください。不明瞭なコメントでごめんなさい。
Rob Evans

2
@maddob私はあなたが言った\ x3を見ることができません:私のエスケープされた文字列は期待通り、見栄えが良いです
フェデリコ

43

ここでの式のほとんどは、単一の特定のユースケースを解決します。

それは大丈夫ですが、私は「常に機能する」アプローチを好みます。

function regExpEscape(literal_string) {
    return literal_string.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g, '\\$&');
}

これは、正規表現での次の使用のためにリテラル文字列を「完全にエスケープ」します。

  • 正規表現への挿入。例えばnew RegExp(regExpEscape(str))
  • 文字クラスへの挿入。例えばnew RegExp('[' + regExpEscape(str) + ']')
  • 整数カウント指定子への挿入。例えばnew RegExp('x{1,' + regExpEscape(str) + '}')
  • 非JavaScript正規表現エンジンでの実行。

対象となる特殊文字:

  • -:文字クラスに文字範囲を作成します。
  • [/ ]:文字クラスを開始/終了します。
  • {/ }:数値指定子を開始/終了します。
  • (/ ):グループを開始/終了します。
  • */ +/ ?:繰り返しタイプを指定します。
  • .:任意の文字に一致します。
  • \:文字をエスケープし、エンティティを開始します。
  • ^:一致するゾーンの開始を指定し、文字クラスでの一致を無効にします。
  • $:一致するゾーンの終了を指定します。
  • |:代替を指定します。
  • #:自由間隔モードでコメントを指定します。
  • \s:自由間隔モードでは無視されます。
  • ,:数値指定子の値を区切ります。
  • /:式を開始または終了します。
  • ::特別なグループタイプと、Perlスタイルの文字クラスの一部を完了します。
  • !:ゼロ幅グループを無効にします。
  • </ =:ゼロ幅グループ仕様の一部。

ノート:

  • /正規表現の種類によっては、厳密には必要ありません。ただし、誰か(震動)が発生した場合に備えて保護しeval("/" + pattern + "/");ます。
  • , 文字列が数値指定子で整数であることを意図している場合、暗黙的に間違ってコンパイルするのではなく、RegExpコンパイルエラーを適切に発生させます。
  • #、および\sJavaScriptでエスケープする必要はありませんが、他の多くのフレーバーでエスケープする必要があります。正規表現が後で別のプログラムに渡される場合に備えて、これらはここでエスケープされます。

JavaScriptの正規表現エンジン機能への追加の可能性に対する正規表現の将来性も保証する必要がある場合は、より偏執的な使用をお勧めします。

function regExpEscapeFuture(literal_string) {
    return literal_string.replace(/[^A-Za-z0-9_]/g, '\\$&');
}

この関数は、将来の正規表現フレーバーの構文で使用されないことが明示的に保証されている文字を除いて、すべての文字をエスケープします。


真の衛生管理のために、次のエッジケースを検討してください。

var s = '';
new RegExp('(choice1|choice2|' + regExpEscape(s) + ')');

これ JavaScriptで正常にコンパイルされますが、他のフレーバーでコンパイルされません。別のフレーバーに渡そうとする場合はs === ''、次のようにnullケースを個別にチェックする必要があります。

var s = '';
new RegExp('(choice1|choice2' + (s ? '|' + regExpEscape(s) : '') + ')');

1
/でエスケープする必要はありません[...]文字クラス。
Dan Dascalescu 2017

1
これらのほとんどはエスケープする必要はありません。「文字クラスの文字範囲を作成します」 -文字列内の文字クラスには決して入りません。「自由間隔モードではコメントを指定し、自由間隔モードでは無視されます」 -JavaScriptではサポートされていません。「数値指定子で値を分離する」 -文字列内の数値指定子には決して入りません。また、名前付け仕様内に任意のテキストを書き込むことはできません。「表現の開始または終了」 -エスケープする必要はありません。はるかにエスケープする必要があるため、Evalはケースではありません。[次のコメントで継続される]
Qwertiy 2017

「完全な特殊グループタイプ、およびPerlスタイルの文字クラスの一部」 -JavaScriptでは使用できないようです。「ゼロ幅グループの否定、ゼロ幅グループ仕様の一部」 -文字列内にグループを含めることはできません。
Qwertiy 2017

@Qwertiyこれらの余分なエスケープの理由は、特定のユースケースで問題を引き起こす可能性があるエッジケースを排除するためです。たとえば、この関数のユーザーは、エスケープされた正規表現文字列をグループの一部として別の正規表現に挿入したり、JavaScript以外の言語で使用したりすることもできます。この関数は一般的なものであるため、「文字クラスの一部になることはありません」などの仮定はありません。よりYAGNIのアプローチについては、他の回答をここで参照してください。
Pi Marillion 2017

とても良い。なぜ_がエスケープされないのですか?おそらくそれが後で正規表現構文にならないことを保証するものは何ですか?
madprops 2017年


21

jQueryUIのオートコンプリートウィジェット(バージョン1.9.1)では、わずかに異なる正規表現(6753行目)を使用しています。これは、@ bobinceアプローチと組み合わせた正規表現です。

RegExp.escape = function( value ) {
     return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
}

4
唯一の違いは、エスケープする,(メタキャラクターではない)#ことと、フリースペースモードでのみ重要な空白(JavaScriptではサポートされない)であることです。ただし、フォワードスラッシュをエスケープしないようにしてください。
マーティンエンダー2013

18
コードをローカルに貼り付けるのではなく、jquery UIの実装を再利用する場合は、を使用してください$.ui.autocomplete.escapeRegex(myString)
スコットスタッフォード

2
lodashにもこれがあります、_。escapeRegExpおよびnpmjs.com/package/lodash.escaperegexp
Ted

v1.12も同じです。
Peter Krauss 2017年

13

英数字以外のすべての文字をエスケープすることを妨げるものはありません。

usersString.replace(/(?=\W)/g, '\\');

実行するとある程度の読みやすさが失われますが、re.toString()かなりの単純さ(およびセキュリティ)が得られます。

ECMA-262によると、一方では、正規表現「の構文文字は、」常に非英数字あり、結果が安全であるように、そして特殊なエスケープシーケンスは(\d\w\n)常に偽制御エスケープが生じないことを英数字のようなものです。


シンプルで効果的。私はこれが受け入れられた答えよりもずっと好きです。(本当に)古いブラウザの場合.replace(/[^\w]/g, '\\$&')、同じように動作します。
Tomas Langkaas 2017

6
これはUnicodeモードでは失敗します。たとえば、サロゲートペアの各コードユニットを個別に照合してnew RegExp('🍎'.replace(/(?=\W)/g, '\\'), 'u')例外をスローし\W、無効なエスケープコードを生成します。
Alexey Lebedev

1
代替案:.replace(/\W/g, "\\$&");
Miguel Pynto 2018年

@AlexeyLebedev彼は答えがユニコードモードを処理するように修正されましたか?または、このシンプルさを維持しながら、他の場所で解決策はありますか?
ジョニーなぜ


6

これは短いバージョンです。

RegExp.escape = function(s) {
    return s.replace(/[$-\/?[-^{|}]/g, '\\$&');
}

これは、非メタ文字が含まれ%&'、と,、しかし、JavaScriptの正規表現の仕様では、これを可能にします。


2
文字範囲が文字のリストを非表示にするため、この「短い」バージョンは使用しません。これにより、一見して正しいかどうかを確認することが難しくなります。
nhahtdh 2014年

@nhahtdh私もおそらくそうは思わないでしょうが、情報のためにここに投稿されています。
kzh 2014年

@kzh:「情報のために」投稿することは、理解するために投稿することよりも役立ちます。私の答えがより明確であることに同意しませんか?
Dan Dascalescu 2014年

少なくとも、.見逃されています。そして()。か否か?[-^奇妙です。何があったのか覚えていません。
Qwertiy 2017

それらは指定された範囲内にあります。
kzh


3

正規表現で問題を引き起こす文字だけをエスケープするのではなく(たとえば、ブラックリスト)、代わりにホワイトリストの使用を検討してください。このように、一致しない限り、各文字は汚染されていると見なされます。

この例では、次の式を想定しています。

RegExp.escape('be || ! be');

これは、文字、数字、スペースをホワイトリストに登録します。

RegExp.escape = function (string) {
    return string.replace(/([^\w\d\s])/gi, '\\$1');
}

戻り値:

"be \|\| \! be"

これはエスケープする必要のない文字をエスケープする可能性がありますが、これはあなたの表現を妨げません(たぶんいくつかのマイナーな時間のペナルティ-しかしそれは安全のために価値があります)。


これは@filipの回答とは異なりますか?stackoverflow.com/a/40562456/209942
ジョニーなぜ


1

他の回答の関数は、正規表現全体をエスケープするためには過剰です(後でより大きな正規表現に連結される正規表現の一部をエスケープするために役立つ場合があります)。

あなたは全体の正規表現をエスケープし、それで行われ、(スタンドアロンているメタ文字を引用した場合は.?+*^$|\何かを)または開始(([{)あなたが必要とするすべてです:

String.prototype.regexEscape = function regexEscape() {
  return this.replace(/[.?+*^$|({[\\]/g, '\\$&');
};

そして、はい、JavaScriptにこのような組み込み関数がないのは残念です。


ユーザー入力をエスケープして次のように(text)next挿入するとします:(?:+ input + )。あなたのメソッドは(?:\(text)next)コンパイルに失敗した結果の文字列を与えます。これはかなり妥当な挿入であり、re\+入力+のようなクレイジーな挿入ではないことに注意してくださいre(この場合、プログラマーは愚かなことをしたとして非難される可能性があります)
nhahtdh

1
@nhahtdh:私の答えは、正規表現全体をエスケープし、正規表現の一部(または将来の部分)ではなく、正規表現で「完了」することを具体的に述べました。親切に反対投票を取り消しますか?
Dan Dascalescu 2014年

式全体をエスケープすることはめったにありません-文字列操作があります。これは、リテラル文字列を操作したい場合、正規表現に比べてはるかに高速です。
nhahtdh 2014年

これは正しくないことについては言及していません- \正規表現が\wそのまま残るため、エスケープする必要があります。また、JavaScriptは末尾を許可しないようです)。少なくとも、Firefoxがエラーをスローするためです。
nhahtdh 2014年

1
クロージングに関する部分に言及してください)
nhahtdh '28

1

別の(はるかに安全な)アプローチは、Unicodeエスケープ形式を使用して、すべての文字(現在わかっているいくつかの特殊な文字だけでなく)をエスケープすることです\u{code}

function escapeRegExp(text) {
    return Array.from(text)
           .map(char => `\\u{${char.charCodeAt(0).toString(16)}}`)
           .join('');
}

console.log(escapeRegExp('a.b')); // '\u{61}\u{2e}\u{62}'

uこのメソッドを機能させるには、フラグを渡す必要があることに注意してください。

var expression = new RegExp(escapeRegExp(usersString), 'u');

1

エスケープする必要のある12のメタキャラクターが存在する
リテラルと見なされるする。

エスケープされた文字列で何が行われるかは関係ありません。
正規表現ラッパーに、追加されても、関係ありません。

これを使用して文字列を置き換えます

var escaped_string = oldstring.replace( /[\\^$.|?*+()[{]/g, '\\$&' );

どう]ですか?
Thomasleveil
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.