Cree RegExps sobre la marcha utilizando variables de cadena

138

Digamos que quería hacer lo siguiente reutilizable:

function replace_foo(target, replacement) {
   return target.replace("string_to_replace",replacement);
}

Podría hacer algo como esto:

function replace_foo(target, string_to_replace, replacement) {
   return target.replace(string_to_replace,replacement);
}

Con literales de cadena esto es bastante fácil. Pero, ¿qué pasa si quiero ser un poco más complicado con la expresión regular? Por ejemplo, digamos que quiero reemplazar todo pero string_to_replace . Instintivamente, trataría de extender lo anterior haciendo algo como:

function replace_foo(target, string_to_replace, replacement) {
   return target.replace(/^string_to_replace/,replacement);
}

Esto no parece funcionar. Supongo que piensa que string_to_replacees un literal de cadena, en lugar de una variable que representa una cadena. ¿Es posible crear expresiones regulares de JavaScript sobre la marcha utilizando variables de cadena? Algo como esto sería genial si es posible:

function replace_foo(target, string_to_replace, replacement) {
   var regex = "/^" + string_to_replace + "/";
   return target.replace(regex,replacement);
}
buley
fuente

Respuestas:

215

Hay new RegExp(string, flags)donde flagsestán go i. Entonces

'GODzilla'.replace( new RegExp('god', 'i'), '' )

evalúa a

zilla
meder omuraliev
fuente
31
Y omita los /delimitadores de expresiones regulares cuando use este formulario también.
cdhowie
111

Con literales de cadena esto es bastante fácil.

¡Realmente no! El ejemplo solo reemplaza la primera aparición de string_to_replace. Por lo general, desea reemplazar todas las ocurrencias, en cuyo caso, debe convertir la cadena en un Global ( /.../g) RegExp. Puedes hacer esto desde una cadena usando el new RegExpconstructor:

new RegExp(string_to_replace, 'g')

El problema con esto es que cualquier carácter especial regex en el literal de cadena se comportará de manera especial en lugar de ser caracteres normales. Tendría que retroceder y escapar de ellos para arreglar eso. Desafortunadamente, no hay una función integrada para hacer esto por usted, así que aquí hay una que puede usar:

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

Tenga en cuenta también que cuando se utiliza una expresión regular en replace()la cadena de reemplazo tiene ahora un carácter especial también $. ¡Esto también se debe escapar si desea tener un literal $en su texto de reemplazo!

function escapeSubstitute(s) {
    return s.replace(/\$/g, '$$$$');
}

(Cuatro $s porque eso es en sí mismo una cadena de reemplazo, ¡argh!)

Ahora puede implementar el reemplazo global de cadenas con RegExp:

function replace_foo(target, string_to_replace, replacement) {
    var relit= escapeRegExp(string_to_replace);
    var sub= escapeSubstitute(replacement);
    var re= new RegExp(relit, 'g');
    return target.replace(re, sub);
}

Que dolor. Afortunadamente, si todo lo que quiere hacer es reemplazar una cadena recta sin partes adicionales de expresiones regulares, hay una manera más rápida:

s.split(string_to_replace).join(replacement)

...y eso es todo. Este es un idioma comúnmente entendido.

decir que quiero reemplazar todo menos string_to_replace

¿Qué significa eso, desea reemplazar todos los tramos de texto que no participan en una coincidencia contra la cadena? Un reemplazo con ^ciertamente no es esto, porque ^significa un token de inicio de cadena, no una negación. ^Es solo una negación en los []grupos de personajes. También hay lookaheads negativos (?!...), pero hay problemas con eso en JScript, por lo que generalmente debe evitarlo.

Puede intentar hacer coincidir 'todo hasta' la cadena y usar una función para descartar cualquier tramo vacío entre cadenas coincidentes:

var re= new RegExp('(.*)($|'+escapeRegExp(string_to_find)+')')
return target.replace(re, function(match) {
    return match[1]===''? match[2] : replacement+match[2];
});

Aquí, nuevamente, una división podría ser más simple:

var parts= target.split(string_to_match);
for (var i= parts.length; i-->0;)
    if (parts[i]!=='')
        parts[i]= replacement;
return parts.join(string_to_match);
bobince
fuente
10

Como han dicho los demás, solía new RegExp(pattern, flags)hacer esto. Vale la pena señalar que pasará literales de cadena a este constructor, por lo que deberá evitarse cada barra diagonal inversa. Si, por ejemplo, desea que su expresión regular coincida con una barra diagonal inversa, necesitaría decir new RegExp('\\\\'), mientras que el literal de expresión regular solo tendría que serlo /\\/. Dependiendo de cómo pretenda usar esto, debe tener cuidado de pasar la entrada del usuario a dicha función sin un preprocesamiento adecuado (escapar caracteres especiales, etc.) Sin esto, sus usuarios pueden obtener algunos resultados muy inesperados.

Kent
fuente
3
Esta respuesta, aunque no es la más detallada, menciona un detalle crucial en el que estuve atrapado durante una hora: escapar de cualquier secuencia especial. Por ejemplo, estaba buscando una palabra que comenzara con un cierto término, así que la expresión regular que necesitaría es /\b[term]\B/, pero al construirla necesito llamar new RegExp("\\b"+ term + "\\B"). Pequeño pero importante diferencia, y es difícil de detectar, ya usarlo como una expresión regular directamente hace el trabajo como se esperaba.
Byson
0

Creo que tengo un muy buen ejemplo para resaltar texto en cadena (se encuentra sin mirar el registro sino resaltado usando el registro)

function getHighlightedText(basicString, filterString) {

    if ((basicString === "") || (basicString === null) || (filterString === "") || (filterString === null)) return basicString;

    return basicString.replace(new RegExp(filterString.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\\\$&'), 'gi'),
        function(match)
            {return "<mark>"+match+"</mark>"});

}

http://jsfiddle.net/cdbzL/1258/

Zhurov Konstantin
fuente
0

Una solución realmente simple para esto es esto:

function replace(target, string_to_replace, replacement) {
  return target.split(string_to_replace).join(replacement);
}

No hay necesidad de expresiones regulares en absoluto

También parece ser el más rápido en los navegadores modernos https://jsperf.com/replace-vs-split-join-vs-replaceall

Jack Allan
fuente