Gibt es eine RegExp.escape-Funktion in Javascript?

442

Ich möchte nur einen regulären Ausdruck aus einer möglichen Zeichenfolge erstellen.

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

Gibt es dafür eine eingebaute Methode? Wenn nicht, was benutzen die Leute? Ruby hat RegExp.escape. Ich habe nicht das Gefühl, dass ich meine eigenen schreiben müsste, da draußen muss es etwas Standardisches geben. Vielen Dank!

Lance Pollard
quelle
15
Ich wollte Sie nur über gute Leute RegExp.escapeauf dem Laufenden halten, an denen gerade gearbeitet wird, und jeder, der glaubt, wertvolle Beiträge zu leisten, ist herzlich eingeladen, einen Beitrag zu leisten. Core-Js und andere Polyfills bieten es an.
Benjamin Gruenbaum
5
Nach der jüngsten Aktualisierung dieser Antwort wurde dieser Vorschlag abgelehnt: Siehe das Problem
try-catch-finally

Antworten:

573

Die oben verlinkte Funktion ist unzureichend. Es kann nicht entkommen ^oder $(Anfang und Ende der Zeichenfolge) oder -, das in einer Zeichengruppe für Bereiche verwendet wird.

Verwenden Sie diese Funktion:

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

Während es auf den ersten Blick unnötig erscheint, macht das Escape -(sowie ^) die Funktion zum Escapezeichen für Zeichen, die in eine Zeichenklasse eingefügt werden sollen, sowie für den Hauptteil des regulären Ausdrucks.

Durch Escaping /eignet sich die Funktion zum Escapezeichen von Zeichen, die in einem JS-Regex-Literal für eine spätere Auswertung verwendet werden sollen.

Da es keinen Nachteil gibt, einem von beiden zu entkommen, ist es sinnvoll, zu entkommen, um breitere Anwendungsfälle abzudecken.

Und ja, es ist ein enttäuschender Fehler, dass dies nicht Teil von Standard-JavaScript ist.

Bobince
quelle
16
Eigentlich brauchen wir nicht zu entkommen /überhaupt
THORN
28
@ Paul: Perl quotemeta( \Q), Python re.escape, PHP preg_quote, Ruby Regexp.quote...
Bobince
13
Wenn Sie diese Funktion in einer Schleife verwenden möchten, ist es wahrscheinlich am besten, das RegExp-Objekt zu einer eigenen Variablen zu machen. var e = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g;Dann lautet Ihre Funktion: Auf return s.replace(e, '\\$&');diese Weise instanziieren Sie das RegExp nur einmal.
Styfle
15
Hier gelten Standardargumente gegen das Erweitern von eingebauten Objekten, nicht wahr? Was passiert, wenn eine zukünftige Version von ECMAScript eine bereitstellt, RegExp.escapederen Implementierung sich von Ihrer unterscheidet? Wäre es nicht besser, wenn diese Funktion an nichts gebunden wäre?
Mark Amery
15
Bobince kümmert sich nicht um die Meinung von
Eslint
113

Für alle, die lodash verwenden, ist seit v3.0.0 eine _.escapeRegExp- Funktion integriert:

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

Und für den Fall, dass Sie nicht die vollständige Lodash-Bibliothek benötigen , benötigen Sie möglicherweise genau diese Funktion !

gustavohenke
quelle
6
Es gibt sogar ein npm-Paket von genau dem! npmjs.com/package/lodash.escaperegexp
Ted Pennings
1
Dadurch werden viele Codes importiert, die für eine so einfache Sache wirklich nicht vorhanden sein müssen. Verwenden Sie die Antwort von Bobince ... funktioniert für mich und es sind so viel weniger Bytes zu laden als für die lodash-Version!
Rob Evans
6
@RobEvans Meine Antwort beginnt mit "Für alle, die lodash verwenden" , und ich erwähne sogar, dass Sie nur die escapeRegExpFunktion benötigen können.
Gustavohenke
2
@gustavohenke Entschuldigung, ich hätte etwas klarer sein sollen, ich habe das Modul, mit dem ich verlinkt bin, in Ihre "genau diese Funktion" aufgenommen, und das habe ich kommentiert. Wenn Sie einen Blick darauf werfen, ist es ziemlich viel Code für eine einzelne Funktion mit einem einzelnen regulären Ausdruck. Stimmen Sie zu, wenn Sie lodash bereits verwenden, dann ist es sinnvoll, es zu verwenden, andernfalls verwenden Sie die andere Antwort. Entschuldigung für den unklaren Kommentar.
Rob Evans
2
@maddob Ich kann das \ x3 nicht sehen, das Sie erwähnt haben: meine entkommenen Zeichenfolgen sehen gut aus, genau das, was ich erwarte
Federico Fissore
43

Die meisten Ausdrücke hier lösen einzelne spezifische Anwendungsfälle.

Das ist okay, aber ich bevorzuge einen "immer funktionierenden" Ansatz.

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

Dadurch wird eine Literalzeichenfolge für eine der folgenden Verwendungen in regulären Ausdrücken "vollständig entzogen":

  • Einfügen in einen regulären Ausdruck. Z.Bnew RegExp(regExpEscape(str))
  • Einfügen in eine Zeichenklasse. Z.Bnew RegExp('[' + regExpEscape(str) + ']')
  • Einfügen in den Integer Count Specifier. Z.Bnew RegExp('x{1,' + regExpEscape(str) + '}')
  • Ausführung in Nicht-JavaScript-Engines für reguläre Ausdrücke.

Abgedeckte Sonderzeichen:

  • -: Erstellt einen Zeichenbereich in einer Zeichenklasse.
  • [/ ]: Startet / beendet eine Zeichenklasse.
  • {/ }: Startet / beendet einen Nummerierungsspezifizierer.
  • (/ ): Startet / beendet eine Gruppe.
  • */ +/ ?: Gibt an Wiederholungstyp.
  • .: Entspricht einem beliebigen Zeichen.
  • \: Entkommt Zeichen und startet Entitäten.
  • ^: Gibt den Beginn der Übereinstimmungszone an und negiert die Übereinstimmung in einer Zeichenklasse.
  • $: Gibt das Ende der Übereinstimmungszone an.
  • |: Gibt den Wechsel an.
  • #: Gibt einen Kommentar im freien Abstand an.
  • \s: Im Freiraummodus ignoriert.
  • ,: Trennt Werte im Nummerierungsspezifizierer.
  • /: Startet oder beendet den Ausdruck.
  • :: Vervollständigt spezielle Gruppentypen und einen Teil der Zeichenklassen im Perl-Stil.
  • !: Negiert die Gruppe mit der Breite Null.
  • </ =: Teil der Gruppenspezifikationen mit der Breite Null.

Anmerkungen:

  • /ist in keinem Geschmack des regulären Ausdrucks unbedingt erforderlich. Es schützt jedoch für den Fall, dass jemand (Schauder) dies tut eval("/" + pattern + "/");.
  • , stellt sicher, dass die Zeichenfolge, wenn sie im numerischen Bezeichner eine Ganzzahl sein soll, ordnungsgemäß einen RegExp-Kompilierungsfehler verursacht, anstatt stillschweigend falsch zu kompilieren.
  • #und \smüssen nicht in JavaScript maskiert werden, sondern in vielen anderen Varianten. Sie werden hier maskiert, falls der reguläre Ausdruck später an ein anderes Programm übergeben wird.

Wenn Sie den regulären Ausdruck auch gegen mögliche Ergänzungen der Funktionen der JavaScript-Regex-Engine zukunftssicher machen müssen, empfehle ich die Verwendung des paranoideren Ausdrucks:

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

Diese Funktion entgeht jedem Zeichen mit Ausnahme derjenigen, die ausdrücklich garantiert nicht für die Syntax in zukünftigen Varianten regulärer Ausdrücke verwendet werden.


Betrachten Sie diesen Randfall für wirklich sanitäre Menschen:

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

Dies sollte in JavaScript gut kompiliert werden können, in einigen anderen Varianten jedoch nicht. Wenn beabsichtigt wird, zu einem anderen Geschmack überzugehen, sollte der Nullfall von s === ''unabhängig überprüft werden, wie folgt:

var s = '';
new RegExp('(choice1|choice2' + (s ? '|' + regExpEscape(s) : '') + ')');
Pi Marillion
quelle
1
Das /muss nicht in der [...]Zeichenklasse maskiert werden.
Dan Dascalescu
1
Die meisten davon müssen nicht entkommen. "Erstellt einen Zeichenbereich in einer Zeichenklasse" - Sie befinden sich niemals in einer Zeichenklasse innerhalb der Zeichenfolge. "Gibt einen Kommentar im freien Abstand an, wird im freien Abstand ignoriert" - wird in Javascript nicht unterstützt. "Trennt Werte im Numerierungsspezifizierer" - Sie befinden sich niemals im Numerarionsspezifizierer innerhalb der Zeichenfolge. Sie können auch keinen beliebigen Text innerhalb der Benennungsspezifikation schreiben. "Startet oder beendet den Ausdruck" - keine Notwendigkeit zu entkommen. Eval ist kein Fall, da es viel mehr Flucht erfordern würde. [wird im nächsten Kommentar fortgesetzt]
Qwertiy
"Vervollständigt spezielle Gruppentypen und Teil von Zeichenklassen im Perl-Stil" - scheint in Javascript nicht verfügbar zu sein. "Negiert die Gruppe mit der Breite Null, Teil der Gruppenspezifikationen mit der Breite Null" - Sie haben niemals Gruppen innerhalb der Zeichenfolge.
Qwertiy
@Qwertiy Der Grund für diese zusätzlichen Escapes ist die Beseitigung von Randfällen, die in bestimmten Anwendungsfällen Probleme verursachen können. Beispielsweise möchte der Benutzer dieser Funktion möglicherweise die maskierte Regex-Zeichenfolge als Teil einer Gruppe in eine andere Regex einfügen oder sie sogar in einer anderen Sprache als Javascript verwenden. Die Funktion macht keine Annahmen wie "Ich werde niemals Teil einer Charakterklasse sein", da sie allgemein gedacht ist . Weitere Informationen zu YAGNI finden Sie in den anderen Antworten hier.
Pi Marillion
Sehr gut. Warum ist _ aber nicht entkommen? Was stellt sicher, dass es später wahrscheinlich nicht zur Regex-Syntax wird?
Madprops
30

Das Mozilla Developer Network-Handbuch für reguläre Ausdrücke bietet diese Escape-Funktion:

function escapeRegExp(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
quietmint
quelle
@ DanDascalescu Du hast recht. Die MDN-Seite wurde aktualisiert und =ist nicht mehr enthalten.
quietmint
21

Im Autocomplete-Widget von jQueryUI (Version 1.9.1) wird ein etwas anderer regulärer Ausdruck verwendet (Zeile 6753). Hier ist der reguläre Ausdruck in Kombination mit dem @ bbince-Ansatz.

RegExp.escape = function( value ) {
     return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
}
Pierluc SS
quelle
4
Der einzige Unterschied besteht darin, dass sie maskieren ,(was kein Metazeichen ist) #und Leerzeichen, die nur im Freiraummodus von Bedeutung sind (der von JavaScript nicht unterstützt wird). Sie machen es jedoch richtig, dem Schrägstrich nicht zu entkommen.
Martin Ender
18
Wenn Sie die Implementierung der jquery-Benutzeroberfläche wiederverwenden möchten, anstatt den Code lokal einzufügen, fahren Sie mit fort $.ui.autocomplete.escapeRegex(myString).
Scott Stafford
2
lodash hat das auch, _. EscapeRegExp und npmjs.com/package/lodash.escaperegexp
Ted Pennings
v1.12 das gleiche, ok!
Peter Krauss
13

Nichts sollte Sie davon abhalten, jedem nicht alphanumerischen Zeichen zu entkommen:

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

Sie verlieren dabei ein gewisses Maß an Lesbarkeit, gewinnen re.toString()aber viel Einfachheit (und Sicherheit).

Nach ECMA-262, auf der einen Seite, sind reguläre Ausdrücke „Syntaxzeichen“ immer nicht-alphanumerische Zeichen, so dass das Ergebnis ist sicher, und ein speziellen Escape - Sequenzen ( \d, \w, \n) sind immer alphanumerische , so dass keine falsche Steuer entweicht produziert werden .

Filip
quelle
Einfach und effektiv. Ich mag das viel besser als die akzeptierte Antwort. Für (wirklich) alte Browser .replace(/[^\w]/g, '\\$&')würde das genauso funktionieren.
Tomas Langkaas
6
Dies schlägt im Unicode-Modus fehl. Löst beispielsweise eine new RegExp('🍎'.replace(/(?=\W)/g, '\\'), 'u')Ausnahme aus, da \Wjede Codeeinheit eines Ersatzpaares separat übereinstimmt, was zu ungültigen Escape-Codes führt.
Alexey Lebedev
1
Alternative:.replace(/\W/g, "\\$&");
Miguel Pynto
@AlexeyLebedev Wurde die Antwort für den Unicode-Modus korrigiert? Oder gibt es anderswo eine Lösung, die diese Einfachheit beibehält?
Johny, warum
6

Dies ist eine kürzere Version.

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

Dazu gehört auch das Nicht-Meta - Zeichen %, &, ', und ,, aber die JavaScript RegExp - Spezifikation ermöglicht dies.

kzh
quelle
2
Ich würde diese "kürzere" Version nicht verwenden, da die Zeichenbereiche die Liste der Zeichen verbergen, was es schwieriger macht, die Richtigkeit auf den ersten Blick zu überprüfen.
nhahtdh
@nhahtdh würde ich wahrscheinlich auch nicht, aber es wird hier zur Information gepostet.
kzh
@kzh: Das Posten "zur Information" hilft weniger als das Posten zum Verständnis. Würden Sie nicht zustimmen, dass meine Antwort klarer ist?
Dan Dascalescu
Zumindest .wird vermisst. Und (). Oder nicht? [-^ist komisch. Ich erinnere mich nicht, was da ist.
Qwertiy
Diese liegen im angegebenen Bereich.
kzh
3

Anstatt nur Zeichen zu maskieren, die Probleme in Ihrem regulären Ausdruck verursachen (z. B. eine schwarze Liste), sollten Sie stattdessen eine Whitelist verwenden. Auf diese Weise wird jedes Zeichen als verdorben betrachtet, sofern es nicht übereinstimmt.

Nehmen Sie für dieses Beispiel den folgenden Ausdruck an:

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

Diese Whitelist enthält Buchstaben, Zahlen und Leerzeichen:

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

Kehrt zurück:

"be \|\| \! be"

Dies kann Zeichen entkommen, die nicht entkommen müssen, aber dies behindert Ihren Ausdruck nicht (möglicherweise einige geringfügige Zeitstrafen - aber es lohnt sich aus Sicherheitsgründen).

Bashaus
quelle
Ist das anders als die Antwort von @ filip? stackoverflow.com/a/40562456/209942
johny warum
3
escapeRegExp = function(str) {
  if (str == null) return '';
  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
};
Ravi Gadhia
quelle
1

Die Funktionen in den anderen Antworten sind übertrieben, um ganze reguläre Ausdrücke zu maskieren (sie können nützlich sein, um Teile regulärer Ausdrücke zu maskieren, die später zu größeren regulären Ausdrücken verkettet werden).

Wenn Sie eine ganze regexp zu entkommen und wird mit ihnen geschehen, die Metazeichen zitiert , die entweder eigenständig sind ( ., ?, +, *, ^, $, |, \) oder beginnt , etwas ( (, [, {) ist alles , was Sie brauchen:

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

Und ja, es ist enttäuschend, dass JavaScript keine solche Funktion hat.

Dan Dascalescu
quelle
Angenommen, Sie entkommen der Benutzereingabe (text)nextund fügen sie ein in: (?:+ Eingabe + ). Ihre Methode gibt die resultierende Zeichenfolge aus, (?:\(text)next)die nicht kompiliert werden kann. Beachten Sie, dass dies eine ziemlich vernünftige Einfügung ist, nicht irgendeine verrückte wie re\+ Eingabe + re(in diesem Fall kann der Programmierer beschuldigt werden, etwas Dummes getan zu haben)
nhahtdh
1
@nhahtdh: In meiner Antwort wurde ausdrücklich erwähnt, dass ganze reguläre Ausdrücke entkommen und mit ihnen "fertig" werden, nicht Teile (oder zukünftige Teile) von regulären Ausdrücken. Bitte machen Sie die Abstimmung rückgängig?
Dan Dascalescu
Es ist selten der Fall, dass Sie dem gesamten Ausdruck entkommen - es gibt Zeichenfolgenoperationen, die im Vergleich zu regulären Ausdrücken viel schneller sind, wenn Sie mit Literalzeichenfolgen arbeiten möchten.
nhahtdh
Dies bedeutet nicht, dass es falsch ist - \sollte maskiert werden, da Ihre Regex \wintakt bleibt. Außerdem scheint JavaScript kein Nachlaufen zuzulassen ), zumindest ist dies der Grund, warum Firefox Fehler auslöst.
nhahtdh
1
Bitte sprechen Sie den Teil über das Schließen)
nhahtdh
1

Ein anderer (viel sicherer) Ansatz besteht darin, alle Zeichen (und nicht nur einige spezielle Zeichen, die wir derzeit kennen) mit dem Unicode-Escape-Format zu maskieren \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}'

Bitte beachten Sie, dass Sie die bestehen müssen u Flag übergeben müssen, damit diese Methode funktioniert:

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

Es gab und wird nur 12 Metazeichen geben, die maskiert werden müssen
, um als Literal betrachtet zu werden.

Es spielt keine Rolle, was mit der maskierten Zeichenfolge gemacht wird, die in einen ausgeglichenen
Regex-Wrapper eingefügt und angehängt wird.

Ersetzen Sie damit einen String

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

quelle
was ist mit ]?
Thomasleveil