¿Existe una función RegExp.escape en Javascript?

442

Solo quiero crear una expresión regular a partir de cualquier cadena posible.

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

¿Hay un método incorporado para eso? Si no, ¿qué usa la gente? Ruby tiene RegExp.escape. No siento que necesite escribir el mío, debe haber algo estándar por ahí. ¡Gracias!

Lance Pollard
fuente
15
Solo quería actualizar a la gente excelente en la que RegExp.escapese trabaja actualmente y cualquier persona que piense que tiene un aporte valioso es bienvenido a contribuir. core-js y otros polyfills lo ofrecen.
Benjamin Gruenbaum
55
De acuerdo con la actualización reciente de esta respuesta, esta propuesta fue rechazada: vea el problema
try-catch-finally

Respuestas:

573

La función vinculada anteriormente es insuficiente. No puede escapar ^o $(inicio y final de la cadena), o -, que en un grupo de caracteres se usa para rangos.

Utiliza esta función:

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

Si bien puede parecer innecesario a primera vista, el escape -(así como ^) hace que la función sea adecuada para insertar caracteres de escape en una clase de caracteres, así como en el cuerpo de la expresión regular.

Escapar /hace que la función sea adecuada para caracteres de escape que se utilizarán en un literal JS regex para una evaluación posterior.

Como no hay inconveniente en escapar de ninguno de ellos, tiene sentido escapar para cubrir casos de uso más amplios.

Y sí, es un fracaso decepcionante que esto no sea parte del JavaScript estándar.

bobince
fuente
16
en realidad, no necesitamos escapar /en absoluto
espinä
28
@Paul: Perl quotemeta( \Q), Python re.escape, PHP preg_quote, Ruby Regexp.quote...
bobince
13
Si va a utilizar esta función en un bucle, probablemente sea mejor hacer que el objeto RegExp sea su propia variable var e = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g;y luego su función es De return s.replace(e, '\\$&');esta manera, solo crea una instancia de RegExp una vez.
styfle
15
Los argumentos estándar contra el aumento de objetos integrados se aplican aquí, ¿no? ¿Qué sucede si una versión futura de ECMAScript proporciona una RegExp.escapecuya implementación difiere de la suya? ¿No sería mejor que esta función no se adjunte a nada?
Mark Amery
15
bobince no se preocupa por la opinión de
eslint
114

Para cualquiera que use lodash, desde v3.0.0 se incluye una función _.escapeRegExp :

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

Y, en el caso de que no desee requerir la biblioteca completa de lodash, ¡puede requerir solo esa función !

gustavohenke
fuente
66
¡incluso hay un paquete npm de solo esto! npmjs.com/package/lodash.escaperegexp
Ted Pennings
1
Esto importa un montón de código que realmente no necesita estar allí para algo tan simple. Use la respuesta de bobince ... ¡funciona para mí y tiene muchos menos bytes para cargar que la versión lodash!
Rob Evans el
66
@RobEvans mi respuesta comienza con "Para cualquiera que use lodash" , e incluso menciono que solo puede requerir la escapeRegExpfunción.
gustavohenke
2
@gustavohenke Lo siento, debería haber sido un poco más claro, incluí el módulo vinculado a su "solo esa función" y eso es lo que estaba comentando. Si echas un vistazo, es bastante código para lo que debería ser efectivamente una sola función con una sola expresión regular. Acepte si ya está usando lodash, entonces tiene sentido usarlo, pero de lo contrario use la otra respuesta. Perdón por el comentario poco claro.
Rob Evans el
2
@maddob No puedo ver eso \ x3 que mencionaste: mis cadenas escapadas se ven bien, justo lo que espero
Federico Fissore
43

La mayoría de las expresiones aquí resuelven casos de uso específicos individuales.

Eso está bien, pero prefiero un enfoque de "siempre funciona".

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

Esto "escapará completamente" de una cadena literal para cualquiera de los siguientes usos en expresiones regulares:

  • Inserción en una expresión regular. P.ejnew RegExp(regExpEscape(str))
  • Inserción en una clase de personaje. P.ejnew RegExp('[' + regExpEscape(str) + ']')
  • Inserción en el especificador de conteo entero. P.ejnew RegExp('x{1,' + regExpEscape(str) + '}')
  • Ejecución en motores de expresión regular que no sean JavaScript.

Caracteres especiales cubiertos:

  • -: Crea un rango de caracteres en una clase de caracteres.
  • [/ ]: Inicia / finaliza una clase de caracteres.
  • {/ }: Inicia / finaliza un especificador de numeración.
  • (/ ): Inicia / finaliza un grupo.
  • */ +/ ?: Especifica el tipo de repetición.
  • .: Coincide con cualquier personaje.
  • \: Escapa de caracteres e inicia entidades.
  • ^: Especifica el inicio de la zona de coincidencia y niega la coincidencia en una clase de caracteres.
  • $: Especifica el final de la zona coincidente.
  • |: Especifica la alternancia.
  • #: Especifica el comentario en el modo de espacio libre.
  • \s: Ignorado en el modo de espacio libre.
  • ,: Separa los valores en el especificador de numeración.
  • /: Inicia o finaliza la expresión.
  • :: Completa tipos de grupos especiales y parte de las clases de personajes de estilo Perl.
  • !: Niega el grupo de ancho cero.
  • </ =: Parte de las especificaciones de grupo de ancho cero.

Notas:

  • /no es estrictamente necesario en ningún sabor de expresión regular. Sin embargo, protege en caso de que alguien (estremecimiento) lo haga eval("/" + pattern + "/");.
  • , garantiza que si la cadena está destinada a ser un número entero en el especificador numérico, provocará correctamente un error de compilación RegExp en lugar de compilar en silencio incorrectamente.
  • #, y \sno necesita ser escapado en JavaScript, pero sí en muchos otros sabores. Aquí se escapan en caso de que la expresión regular se pase luego a otro programa.

Si también necesita probar la expresión regular en el futuro contra posibles adiciones a las capacidades del motor de expresión regular de JavaScript, le recomiendo usar el más paranoico:

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

Esta función escapa a todos los caracteres, excepto aquellos explícitamente garantizados que no se utilizarán para la sintaxis en futuros sabores de expresiones regulares.


Para los verdaderamente interesados ​​en el saneamiento, considere este caso extremo:

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

Esto debería compilarse bien en JavaScript, pero no en otros sabores. Si tiene la intención de pasar a otro sabor, el caso nulo de s === ''debe verificarse de forma independiente, así:

var s = '';
new RegExp('(choice1|choice2' + (s ? '|' + regExpEscape(s) : '') + ')');
Pi Marillion
fuente
1
El /no necesita ser escapado de la [...]clase de caracteres.
Dan Dascalescu
1
La mayoría de estos no necesita ser escapado. "Crea un rango de caracteres en una clase de caracteres" : nunca se encuentra en una clase de caracteres dentro de la cadena. "Especifica comentario en modo de espacio libre, Ignorado en modo de espacio libre" - no es compatible con javascript. "Separa los valores en el especificador de numeración" : nunca está en el especificador de numeración dentro de la cadena. Además, no puede escribir texto arbitrario dentro de la especificación de nombre. "Inicia o termina la expresión" : no es necesario escapar. Eval no es un caso, ya que requeriría mucho más escape. [continuará en el próximo comentario]
Qwertiy
"Completa tipos de grupos especiales y parte de las clases de caracteres de estilo Perl" , parece que no está disponible en javascript. "Niega el grupo de ancho cero, Parte de las especificaciones del grupo de ancho cero" : nunca tiene grupos dentro de la cadena.
Qwertiy
@Qwertiy La razón de estos escapes adicionales es eliminar los casos extremos que podrían causar problemas en ciertos casos de uso. Por ejemplo, el usuario de esta función puede querer insertar la cadena de expresiones regulares escapadas en otra expresión regular como parte de un grupo, o incluso para usarla en otro idioma además de Javascript. La función no hace suposiciones como "Nunca seré parte de una clase de caracteres", porque debe ser general . Para un enfoque más YAGNI, vea cualquiera de las otras respuestas aquí.
Pi Marillion
Muy bien. ¿Por qué no se escapa _? ¿Qué asegura que probablemente no se convertirá en sintaxis de expresiones regulares más adelante?
madprops
21

En el widget de autocompletado de jQueryUI (versión 1.9.1) usan una expresión regular ligeramente diferente (Línea 6753), aquí está la expresión regular combinada con el enfoque @bobince.

RegExp.escape = function( value ) {
     return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
}
Pierluc SS
fuente
44
La única diferencia es que escapan ,(que no es un metacarácter) #y espacios en blanco que solo importan en el modo de espacio libre (que no es compatible con JavaScript). Sin embargo, hacen bien en no escapar de la barra diagonal.
Martin Ender
18
Si desea reutilizar la implementación de jquery UI en lugar de pegar el código localmente, vaya con $.ui.autocomplete.escapeRegex(myString).
Scott Stafford
2
lodash también tiene esto, _. escapeRegExp y npmjs.com/package/lodash.escaperegexp
Ted Pennings
v1.12 lo mismo, ok!
Peter Krauss
13

Nada debería evitar que escapes de todos los caracteres no alfanuméricos:

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

Pierdes un cierto grado de legibilidad al hacerlo, re.toString()pero ganas mucha simplicidad (y seguridad).

De acuerdo con ECMA-262, por un lado, la expresión "caracteres de sintaxis" regulares son siempre no alfanumérico, tal que el resultado es seguro, y secuencias de escape especiales ( \d, \w, \n) siempre son alfanuméricos de tal manera que no se producirán fugas de control falsos .

filip
fuente
Simple y efectivo. Me gusta mucho más que la respuesta aceptada. Para los navegadores (realmente) antiguos, .replace(/[^\w]/g, '\\$&')funcionaría de la misma manera.
Tomas Langkaas
66
Esto falla en modo Unicode. Por ejemplo, new RegExp('🍎'.replace(/(?=\W)/g, '\\'), 'u')arroja una excepción porque \Wcoincide con cada unidad de código de un par sustituto por separado, lo que resulta en códigos de escape no válidos.
Alexey Lebedev
1
alternativa:.replace(/\W/g, "\\$&");
Miguel Pynto
@AlexeyLebedev ¿Se ha solucionado la respuesta para manejar el modo Unicode? ¿O hay una solución en otro lugar que lo hace, manteniendo esta simplicidad?
johny por qué
6

Esta es una versión más corta.

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

Esto incluye los caracteres que no son de meta %, &, ', y ,, aunque la especificación JavaScript RegExp lo permite.

kzh
fuente
2
No usaría esta versión "más corta", ya que los rangos de caracteres ocultan la lista de caracteres, lo que hace que sea más difícil verificar la corrección a primera vista.
nhahtdh
@nhahtdh Probablemente tampoco lo haría, pero está publicado aquí para obtener información.
kzh
@kzh: publicar "para obtener información" ayuda menos que publicar para comprender. ¿No estaría de acuerdo con que mi respuesta es más clara?
Dan Dascalescu
Al menos, .se echa de menos. Y (). ¿O no? [-^es extraño. No recuerdo lo que hay ahí.
Qwertiy
Esos están en el rango especificado.
kzh
3

En lugar de solo caracteres de escape que causarán problemas en su expresión regular (por ejemplo: una lista negra), ¿por qué no considerar usar una lista blanca? De esta manera, cada personaje se considera contaminado a menos que coincida.

Para este ejemplo, suponga la siguiente expresión:

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

Esto incluye letras blancas, números y espacios:

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

Devoluciones:

"be \|\| \! be"

Esto puede escapar de los personajes que no es necesario escapar, pero esto no obstaculiza su expresión (quizás algunas penalizaciones menores de tiempo, pero vale la pena por seguridad).

bashaus
fuente
¿La suya es diferente a la respuesta de @filip? stackoverflow.com/a/40562456/209942
johny why
3
escapeRegExp = function(str) {
  if (str == null) return '';
  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
};
Ravi Gadhia
fuente
1

Las funciones en las otras respuestas son excesivas para escapar de expresiones regulares enteras (pueden ser útiles para escapar de partes de expresiones regulares que luego se concatenarán en expresiones regulares más grandes).

Si escapar de una expresión regular entero y terminado con él, citando a los meta-caracteres que son o independiente ( ., ?, +, *, ^, $, |, \) o iniciar algo ( (, [, {) es todo lo que necesita:

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

Y sí, es decepcionante que JavaScript no tenga una función como esta incorporada.

Dan Dascalescu
fuente
Supongamos que escapa de la entrada del usuario (text)nexty la inserta en: (?:+ input + ). Su método le dará la cadena resultante (?:\(text)next)que no se compila. Tenga en cuenta que esta es una inserción bastante razonable, no una loca como re\+ input + re(en este caso, se puede culpar al programador por hacer algo estúpido)
nhahtdh
1
@nhahtdh: mi respuesta mencionó específicamente escapar de expresiones regulares enteras y "terminar" con ellas, no partes (o partes futuras) de expresiones regulares. ¿Deshacer amablemente el voto negativo?
Dan Dascalescu
Rara vez se da el caso de que escape de toda la expresión: hay una operación de cadena, que es mucho más rápida en comparación con la expresión regular si desea trabajar con una cadena literal.
nhahtdh
Esto no menciona que sea incorrecto: \debe escapar, ya que su expresión regular se dejará \wintacta. Además, JavaScript no parece permitir el rastreo ), al menos para eso es que Firefox arroja el error.
nhahtdh
1
Por favor, aborde la parte sobre el cierre)
nhahtdh
1

Otro enfoque (mucho más seguro) es escapar de todos los caracteres (y no solo de algunos especiales que conocemos actualmente) utilizando el formato de 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}'

Tenga en cuenta que debe pasar la ubandera para que este método funcione:

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

Solo ha habido y habrá 12 metacaracteres que deben escaparse
para considerarse literal.

No importa lo que se haga con la cadena escapada, insertada en una
envoltura de expresiones regulares equilibrada , añadida, no importa.

Haga un reemplazo de cadena usando esto

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

fuente
¿qué pasa ]?
Thomasleveil