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?

Gordon Gustafson
fuente

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;
Gumbo
fuente
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]);
Anónimo
fuente
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
Rivenfall
fuente
15

Use el constructor de objetos JavaScript RegExp .

var re = new RegExp("\\w+");
re.test("hello");

Puede pasar banderas como un segundo argumento de cadena al constructor. Consulte la documentación para más detalles.

Ayman Hourieh
fuente
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);
}
staabm
fuente
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).

Stephan202
fuente
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'));
            

kofifus
fuente
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)
}
Richie Bendall
fuente
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, '');
Gene Bo
fuente
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);
Pim Jager
fuente
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()));
    }
Playhi
fuente
3
eval on userInput es un riesgo de seguridad loco
Samuel Faure
1
Sr. Bobby Mesas!
Luiz Felipe