Esiste una funzione RegExp.escape in Javascript?


443

Voglio solo creare un'espressione regolare da qualsiasi possibile stringa.

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

Esiste un metodo integrato per quello? In caso contrario, cosa usano le persone? Ha Ruby RegExp.escape. Non mi sento di dover scrivere il mio, ci deve essere qualcosa di standard là fuori. Grazie!


15
Volevo solo aggiornare la tua brava gente su cui si RegExp.escapesta attualmente lavorando e chiunque pensi di avere un input prezioso è il benvenuto. core-js e altri polyfill lo offrono.
Benjamin Gruenbaum,

5
Secondo il recente aggiornamento di questa risposta questa proposta è stata respinta: vedi il problema
try-catch-finally

Risposte:


574

La funzione collegata sopra non è sufficiente. Non riesce a fuggire ^o $(inizio e fine della stringa) o -, che in un gruppo di caratteri viene utilizzato per gli intervalli.

Usa questa funzione:

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

Mentre a prima vista può sembrare superfluo, l'escaping -(così come ^) rende la funzione adatta a caratteri di escape da inserire in una classe di caratteri e nel corpo della regex.

L' /escaping rende la funzione adatta a caratteri di escape da usare in un regex letterale JS per l'eval successivo.

Dato che non c'è alcun aspetto negativo nel fuggire da entrambi, ha senso scappare per coprire casi d'uso più ampi.

E sì, è un deludente fallimento che questo non faccia parte di JavaScript standard.


16
in realtà, non abbiamo bisogno di scappare /affatto
thorn̈

28
@Paul: Perl quotemeta( \Q), Python re.escape, PHP preg_quote, Ruby Regexp.quote...
bobince

13
Se hai intenzione di usare questa funzione in un ciclo, probabilmente è meglio rendere l'oggetto RegExp la propria variabile var e = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g;e quindi la tua funzione è In return s.replace(e, '\\$&');questo modo puoi creare un'istanza di RegExp solo una volta.
styfle,

15
Qui si applicano argomenti standard contro l'aumento degli oggetti incorporati, no? Cosa succede se una versione futura di ECMAScript fornisce una RegExp.escapecui implementazione differisce dalla tua? Non sarebbe meglio che questa funzione non fosse collegata a nulla?
Mark Amery,

15
non cure bobince per di eslint opinione
bobince

115

Per chiunque usi lodash, poiché v3.0.0 è integrata una funzione _.escapeRegExp :

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

E, nel caso in cui non si desideri richiedere la libreria lodash completa, è possibile richiedere proprio quella funzione !


6
c'è persino un pacchetto npm di questo! npmjs.com/package/lodash.escaperegexp
Ted Pennings

1
Questo importa un sacco di codice che non ha davvero bisogno di essere lì per una cosa così semplice. Usa la risposta di Bobince ... funziona per me e per i suoi molti meno byte da caricare rispetto alla versione lodash!
Rob Evans,

6
@RobEvans la mia risposta inizia con "Per chiunque usi lodash" , e ho anche detto che puoi richiedere solo la escapeRegExpfunzione.
gustavohenke,

2
@gustavohenke Mi dispiace che avrei dovuto essere leggermente più chiaro, ho incluso il modulo collegato nella tua "solo quella funzione" ed è quello che stavo commentando. Se dai un'occhiata è un sacco di codice per quella che dovrebbe essere effettivamente una singola funzione con una sola regexp al suo interno. D'accordo se stai già usando lodash, allora ha senso usarlo, ma usa l'altra risposta. Ci scusiamo per il commento poco chiaro.
Rob Evans,

2
@maddob Non riesco a vedere che \ x3 hai menzionato: le mie stringhe sfuggite stanno bene, proprio quello che mi aspetto
Federico Fissore

43

La maggior parte delle espressioni qui risolve singoli casi d'uso specifici.

Va bene, ma preferisco un approccio "sempre funzionante".

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

Ciò "sfuggirà completamente" a una stringa letterale per uno dei seguenti usi nelle espressioni regolari:

  • Inserimento in un'espressione regolare. Per esempionew RegExp(regExpEscape(str))
  • Inserimento in una classe di caratteri. Per esempionew RegExp('[' + regExpEscape(str) + ']')
  • Inserimento nello specificatore del numero intero. Per esempionew RegExp('x{1,' + regExpEscape(str) + '}')
  • Esecuzione in motori di espressione regolare non JavaScript.

Personaggi speciali coperti:

  • -: Crea un intervallo di caratteri in una classe di caratteri.
  • [/ ]: Avvia / termina una classe di caratteri.
  • {/ }: Avvia / termina un identificatore di numerazione.
  • (/ ): Avvia / termina un gruppo.
  • */ +/ ?: Specifica il tipo di ripetizione.
  • .: Corrisponde a qualsiasi personaggio.
  • \: Consente di sfuggire ai caratteri e di avviare entità.
  • ^: Specifica l'inizio della zona di corrispondenza e annulla la corrispondenza in una classe di caratteri.
  • $: Specifica la fine della zona corrispondente.
  • |: Specifica l'alternanza.
  • #: Specifica il commento in modalità di spaziatura libera.
  • \s: Ignorato in modalità di spaziatura libera.
  • ,: Separa i valori nell'identificatore di numerazione.
  • /: Avvia o termina l'espressione.
  • :: Completa tipi di gruppo speciali e parte delle classi di caratteri in stile Perl.
  • !: Nega il gruppo di larghezza zero.
  • </ =: Parte delle specifiche del gruppo di larghezza zero.

Appunti:

  • /non è strettamente necessario in nessun tipo di espressione regolare. Tuttavia, protegge in caso qualcuno (brivido) fa eval("/" + pattern + "/");.
  • , assicura che se la stringa deve essere un numero intero nello specificatore numerico, causerà correttamente un errore di compilazione RegExp invece di una compilazione silenziosa errata.
  • #e \snon è necessario eseguire l'escape in JavaScript, ma in molti altri gusti. Vengono salvati qui nel caso in cui l'espressione regolare venga successivamente passata a un altro programma.

Se hai anche bisogno di rendere l'espressione regolare a prova di futuro contro potenziali aggiunte alle funzionalità del motore regex di JavaScript, ti consiglio di usare il più paranoico:

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

Questa funzione sfugge a tutti i caratteri ad eccezione di quelli esplicitamente garantiti che non possono essere utilizzati per la sintassi nei futuri gusti di espressioni regolari.


Per gli appassionati di servizi igienico-sanitari, considera questo caso limite:

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

Questo dovrebbe compilare bene in JavaScript, ma non in alcuni altri gusti. Se si intende passare a un altro sapore, il caso nullo di s === ''deve essere verificato in modo indipendente, in questo modo:

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

1
Il /non ha bisogno di essere sfuggito nella [...]classe di caratteri.
Dan Dascalescu il

1
La maggior parte di questi non deve essere sfuggita. "Crea un intervallo di caratteri in una classe di caratteri" : non ci si trova mai in una classe di caratteri all'interno della stringa. "Specifica il commento in modalità di spaziatura libera, Ignorato in modalità di spaziatura libera" - non supportato in JavaScript. "Separa i valori nello specificatore di numerazione" : non ci si trova mai nello specificatore di numerarion all'interno della stringa. Inoltre, non è possibile scrivere testo arbitrario all'interno delle specifiche di denominazione. "Inizia o termina l'espressione" - non è necessario scappare. Eval non è un caso, poiché richiederebbe molta più fuga. [continuerà nel prossimo commento]
Qwertiy,

"Completa tipi di gruppi speciali e parte di classi di caratteri in stile Perl" - non sembra disponibile in JavaScript. "Nega il gruppo di larghezza zero, parte delle specifiche del gruppo di larghezza zero" - non hai mai gruppi all'interno della stringa.
Qwertiy,

@Qwertiy Il motivo di queste fughe extra è l'eliminazione dei casi limite che potrebbero causare problemi in alcuni casi d'uso. Ad esempio, l'utente di questa funzione potrebbe voler inserire la stringa regex con escape in un'altra regex come parte di un gruppo, o anche per l'uso in un'altra lingua oltre Javascript. La funzione non fa ipotesi come "Non farò mai parte di una classe di personaggi", perché è pensata per essere generale . Per un approccio più YAGNI, vedi una qualsiasi delle altre risposte qui.
Pi Marillion,

Molto bene. Perché _ non è sfuggito però? Cosa garantisce che probabilmente non diventerà sintassi regex in seguito?
madprops,


21

Nel widget di completamento automatico di jQueryUI (versione 1.9.1) usano una regex leggermente diversa (Linea 6753), ecco l'espressione regolare combinata con l'approccio @bobince.

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

4
L'unica differenza è che fuggono ,(che non è un metacarattere) #e spazi bianchi che contano solo in modalità di spaziatura libera (che non è supportata da JavaScript). Tuttavia, fanno bene a non sfuggire alla barra.
Martin Ender,

18
Se si desidera riutilizzare l'implementazione dell'interfaccia utente di jquery anziché incollare il codice localmente, procedere con $.ui.autocomplete.escapeRegex(myString).
Scott Stafford,

2
Lodash ha anche questo, _. escapeRegExp e npmjs.com/package/lodash.escaperegexp
Ted Pennings

v1.12 lo stesso, ok!
Peter Krauss,

13

Nulla dovrebbe impedirti di sfuggire a ogni carattere non alfanumerico:

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

Perdi un certo grado di leggibilità quando lo fai, re.toString()ma vinci molta semplicità (e sicurezza).

Secondo ECMA-262, da un lato, regolare espressione "personaggi sintassi" sono sempre non alfanumerico, tale che il risultato sia sicura, e le sequenze di escape ( \d, \w, \n) sono sempre alfanumerico tali che non ci siano perdite di controllo falsi saranno prodotte .


Semplice ed efficace Mi piace molto meglio della risposta accettata. Per i browser (davvero) vecchi, .replace(/[^\w]/g, '\\$&')funzionerebbe allo stesso modo.
Tomas Langkaas,

6
Questo non riesce in modalità Unicode. Ad esempio, new RegExp('🍎'.replace(/(?=\W)/g, '\\'), 'u')genera un'eccezione perché \Wcorrisponde a ciascuna unità di codice di una coppia surrogata separatamente, generando codici di escape non validi.
Alexey Lebedev,

1
alternativa:.replace(/\W/g, "\\$&");
Miguel Pynto,

@AlexeyLebedev È stata corretta la risposta per gestire la modalità Unicode? Oppure esiste una soluzione altrove, pur mantenendo questa semplicità?
johny perché il


6

Questa è una versione più breve.

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

Questo include i caratteri non-meta di %, &, ', e ,, ma la specifica JavaScript RegExp consente questo.


2
Non userei questa versione "più corta", poiché le gamme di caratteri nascondono l'elenco dei caratteri, il che rende più difficile verificare la correttezza a prima vista.
nhahtdh

@nhahtdh Probabilmente non lo farei neanche io, ma è pubblicato qui per informazione.
kzh

@kzh: pubblicare "per informazione" aiuta meno della pubblicazione per capire. Non saresti d'accordo sul fatto che la mia risposta sia più chiara?
Dan Dascalescu

Almeno, .è mancato. E (). O no? [-^è strano. Non ricordo cosa c'è.
Qwertiy,

Quelli sono nell'intervallo specificato.
kzh,


3

Invece di sfuggire solo ai personaggi che causeranno problemi nella tua espressione regolare (ad esempio: una lista nera), perché non prendere in considerazione l'uso di una whitelist. In questo modo ogni personaggio è considerato contaminato a meno che non corrisponda.

Per questo esempio, assumere la seguente espressione:

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

Questo autorizza le lettere, il numero e gli spazi:

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

Ritorna:

"be \|\| \! be"

Questo può sfuggire ai personaggi che non hanno bisogno di essere sfuggiti, ma ciò non ostacola la tua espressione (forse alcune penalità di tempo minori - ma ne vale la pena per sicurezza).


La sua è diversa dalla risposta di @ filip? stackoverflow.com/a/40562456/209942
Johny perché

3
escapeRegExp = function(str) {
  if (str == null) return '';
  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
};

1

Le funzioni nelle altre risposte sono eccessive per sfuggire a intere espressioni regolari (possono essere utili per sfuggire a parti di espressioni regolari che verranno successivamente concatenate in regexps più grandi).

Se si sfuggire una intera espressione regolare e finito con essa, citando i metacaratteri che sono o standalone ( ., ?, +, *, ^, $, |, \) o inizia qualcosa ( (, [, {) è tutto ciò che serve:

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

E sì, è deludente che JavaScript non abbia una funzione come questa integrata.


Diciamo che si evita l'input dell'utente (text)nexte lo si inserisce in: (?:+ input + ). Il metodo fornirà la stringa risultante (?:\(text)next)che non riesce a compilare. Si noti che questo è un inserimento abbastanza ragionevole, non un pazzo come re\+ input + re(in questo caso, il programmatore può essere accusato di aver fatto qualcosa di stupido)
nhahtdh,

1
@nhahtdh: la mia risposta menzionava specificamente la fuga da intere espressioni regolari e il "fatto" con esse, non parti (o parti future) di regexps. Si prega di annullare il downvote?
Dan Dascalescu,

Raramente è possibile sfuggire all'intera espressione: ci sono operazioni con le stringhe, che sono molto più veloci rispetto a regex se si desidera lavorare con una stringa letterale.
nhahtdh,

Questo non significa che sia errato - \dovrebbe essere evitato, poiché il tuo regex lascerà \wintatto. Inoltre, JavaScript non sembra consentire il trailing ), almeno questo è ciò per cui Firefox genera errori.
nhahtdh,

1
Si prega di affrontare la parte sulla chiusura)
nhahtdh,

1

Un altro approccio (molto più sicuro) è quello di sfuggire a tutti i personaggi (e non solo a quelli speciali che attualmente conosciamo) usando il formato di escape 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}'

Si noti che è necessario passare il uflag per far funzionare questo metodo:

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

1

Ci sono sempre stati e ci saranno mai 12 meta personaggi che devono essere evasi
per essere considerati letterali.

Non importa cosa viene fatto con la stringa di escape, inserita in un
wrapper regex bilanciato , aggiunto, non importa.

Sostituisci usando una stringa usando questo

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

che dire ]?
Thomasleveil,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.