Conversión de cadena de entrada de usuario a expresión regular


333

Estoy diseñando un probador de expresiones regulares en HTML y JavaScript. El usuario ingresará una expresión regular, una cadena y elegirá la función con la que desea probar (por ejemplo, buscar, igualar, reemplazar, etc.) mediante un botón de opción y el programa mostrará los resultados cuando esa función se ejecute con los argumentos especificados. Naturalmente, habrá cuadros de texto adicionales para reemplazar los argumentos adicionales y demás.

Mi problema es obtener la cadena del usuario y convertirla en una expresión regular. Si digo que no necesitan tener //alrededor de la expresión regular que ingresan, entonces no pueden establecer banderas, como gy i. Entonces tienen que tener los //'s alrededor de la expresión, pero ¿cómo puedo convertir esa cadena en una expresión regular? No puede ser un literal ya que es una cadena, y no puedo pasarlo al constructor RegExp ya que no es una cadena sin el //'s. ¿Hay alguna otra forma de convertir una cadena de entrada de usuario en una expresión regular? ¿Tendré que analizar la cadena y las banderas de la expresión regular con los //'s y luego construirla de otra manera? ¿Debo hacer que ingresen una cadena y luego ingresen las banderas por separado?

Respuestas:


611

Use el constructor de objetos RegExp para crear una expresión regular a partir de una cadena:

var re = new RegExp("a|b", "i");
// same as
var re = /a|b/i;

1
sería bueno tener una herramienta en línea con un campo de entrada
holms

61
Al hacerlo de esta manera, debe escapar de la barra diagonal inversa, por ejemplovar re = new RegExp("\\w+");
JD Smith

12
@holms regex101.com es una gran herramienta en línea de expresiones regulares también
Fran Herrero

2
Me tomó un tiempo ver que no se requieren cortes finales
Gerfried

2
@JDSmith No quise decirlo en tu ejemplo. Quise decir que debe escapar de las comillas dobles si desea que formen parte de la expresión regular, siempre que esté codificada. Obviamente, nada de esto se aplica si la cadena está en una variable como una <input>etiqueta HTML. var re = new RegExp("\"\\w+\"");es un ejemplo de una expresión regular codificada usando el constructor RegExp y es necesario el escape de las comillas dobles . Lo que quiero decir con una cadena en una variable es que solo puede hacer var re = new RegExp(str);y strpuede contener comillas dobles o barras invertidas sin problema.
Luis Paulo

66
var flags = inputstring.replace(/.*\/([gimy]*)$/, '$1');
var pattern = inputstring.replace(new RegExp('^/(.*?)/'+flags+'$'), '$1');
var regex = new RegExp(pattern, flags);

o

var match = inputstring.match(new RegExp('^/(.*?)/([gimy]*)$'));
// sanity check here
var regex = new RegExp(match[1], match[2]);

Debe considerar que /\/se reconoce una entrada no válida como .
Gumbo

8
O deje que el constructor RegExp falle, "siguiendo \ en expresión regular", en lugar de escribir un analizador complicado.
Anónimo

21

Aquí hay una frase: str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')

Lo obtuve del módulo NPM escape-string-regexp .

Probándolo:

escapeStringRegExp.matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
function escapeStringRegExp(str) {
    return str.replace(escapeStringRegExp.matchOperatorsRe, '\\$&');
}

console.log(new RegExp(escapeStringRegExp('example.com')));
// => /example\.com/

Uso de literales de plantilla etiquetados con soporte de banderas:

function str2reg(flags = 'u') {
    return (...args) => new RegExp(escapeStringRegExp(evalTemplate(...args))
        , flags)
}

function evalTemplate(strings, ...values) {
    let i = 0
    return strings.reduce((str, string) => `${str}${string}${
        i < values.length ? values[i++] : ''}`, '')
}

console.log(str2reg()`example.com`)
// => /example\.com/u


9

En mi caso, la entrada del usuario a veces estaba rodeada de delimitadores y, a veces, no. por eso agregué otro caso ...

var regParts = inputstring.match(/^\/(.*?)\/([gim]*)$/);
if (regParts) {
    // the parsed pattern had delimiters and modifiers. handle them. 
    var regexp = new RegExp(regParts[1], regParts[2]);
} else {
    // we got pattern string without delimiters
    var regexp = new RegExp(inputstring);
}

3
siempre puedes usar la .split()función en lugar de una larga cadena de expresiones regulares. regParts = inputstring.split('/')esto haría que regParts[1]la cadena de expresiones regulares y regParts[2]los delimitadores (suponiendo que la configuración de la expresión regular sea /.../gim). Puede comprobar si hay delimitadores con regParts[2].length < 0.
Jaketr00

3

Le sugiero que también agregue casillas de verificación separadas o un campo de texto para las banderas especiales. De esta manera, está claro que el usuario no necesita agregar ninguno //. En el caso de un reemplazo, proporcione dos campos de texto. Esto hará tu vida mucho más fácil.

¿Por qué? Porque de lo contrario, algunos usuarios agregarán //'s' mientras que otros no. Y algunos harán un error de sintaxis. Luego, después de despojar los //'s, puede terminar con una expresión regular sintácticamente válida que no se parece en nada a lo que el usuario pretendía, lo que lleva a un comportamiento extraño (desde la perspectiva del usuario).


2

Esto funcionará también cuando la cadena no sea válida o no contenga banderas, etc.

function regExpFromString(q) {
  let flags = q.replace(/.*\/([gimuy]*)$/, '$1');
  if (flags === q) flags = '';
  let pattern = (flags ? q.replace(new RegExp('^/(.*?)/' + flags + '$'), '$1') : q);
  try { return new RegExp(pattern, flags); } catch (e) { return null; }
}

console.log(regExpFromString('\\bword\\b'));
console.log(regExpFromString('\/\\bword\\b\/gi'));
            


2

Si realmente desea convertir una cadena en una expresión regular, intente usar la siguiente función:

function String2Regex(s){return new RegExp(s.match(/\/(.+)\/.*/)[1], s.match(/\/.+\/(.*)/)[1]);}

Puedes usarlo así:

"abc".match(String2Regex("/a/g"))
> ["a"]

Como referencia, aquí está la versión formateada y más moderna:

const String2Regex = str => {
  // Main regex
  const main = str.match(/\/(.+)\/.*/)[1]

  // Regex options
  const options = str.match(/\/.+\/(.*)/)[1]

  // Return compiled regex
  return new RegExp(main, options)
}

1

Gracias a las respuestas anteriores, este bloque sirve como una solución de propósito general para aplicar una cadena configurable en un RegEx ... para filtrar texto:

var permittedChars = '^a-z0-9 _,.?!@+<>';
permittedChars = '[' + permittedChars + ']';

var flags = 'gi';
var strFilterRegEx = new RegExp(permittedChars, flags);

log.debug ('strFilterRegEx: ' + strFilterRegEx);

strVal = strVal.replace(strFilterRegEx, '');
// this replaces hard code solt:
// strVal = strVal.replace(/[^a-z0-9 _,.?!@+]/ig, '');

1

Puedes pedir banderas usando casillas de verificación y luego hacer algo como esto:

var userInput = formInput;
var flags = '';
if(formGlobalCheckboxChecked) flags += 'g';
if(formCaseICheckboxChecked) flags += 'i';
var reg = new RegExp(userInput, flags);

parece que a RegEx le falta el p final . Stack no me permitió hacer una edición de 1 personaje
Gene Bo

-3

Yo uso evalpara resolver este problema.

Por ejemplo:

    function regex_exec() {

        // Important! Like @Samuel Faure mentioned, Eval on user input is a crazy security risk, so before use this method, please take care of the security risk. 
        var regex = $("#regex").val();

        // eval()
        var patt = eval(userInput);

        $("#result").val(patt.exec($("#textContent").val()));
    }

3
eval on userInput es un riesgo de seguridad loco
Samuel Faure

1
Sr. Bobby Mesas!
Luiz Felipe
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.