Conversion d'une chaîne d'entrée utilisateur en expression régulière


333

Je conçois un testeur d'expressions régulières en HTML et JavaScript. L'utilisateur entrera une expression rationnelle, une chaîne et choisira la fonction avec laquelle il veut tester (par exemple rechercher, faire correspondre, remplacer, etc.) via le bouton radio et le programme affichera les résultats lorsque cette fonction sera exécutée avec les arguments spécifiés. Naturellement, il y aura des zones de texte supplémentaires pour les arguments supplémentaires à remplacer et autres.

Mon problème est d'obtenir la chaîne de l'utilisateur et de la transformer en une expression régulière. Si je dis qu'ils n'ont pas besoin d'avoir //autour de l'expression régulière dans laquelle ils entrent, ils ne peuvent pas définir de drapeaux, comme get i. Ils doivent donc avoir le //'s autour de l'expression, mais comment puis-je convertir cette chaîne en expression régulière? Ce ne peut pas être un littéral car c'est une chaîne, et je ne peux pas le passer au constructeur RegExp car ce n'est pas une chaîne sans le //. Existe-t-il un autre moyen de transformer une chaîne d'entrée utilisateur en expression régulière? Dois-je analyser la chaîne et les drapeaux de l'expression rationnelle avec le //s, puis le construire d'une autre manière? Dois-je les faire entrer une chaîne, puis entrer les drapeaux séparément?

Réponses:


611

Utilisez le constructeur d'objet RegExp pour créer une expression régulière à partir d'une chaîne:

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

1
serait bien d'avoir un outil en ligne avec un champ de saisie
holms

61
Lorsque vous le faites de cette façon, vous devez échapper à la barre oblique inverse, par exemplevar re = new RegExp("\\w+");
JD Smith

12
@holms regex101.com est aussi un excellent outil en ligne regex
Fran Herrero

2
Il m'a fallu un certain temps pour voir qu'il n'y avait pas de barres obliques de fin requises
Gerfried

2
@JDSmith Je ne le pensais pas dans votre exemple. Je voulais dire que vous devez échapper aux guillemets doubles si vous voulez qu'ils fassent partie de l'expression régulière à condition qu'il soit codé en dur. Évidemment, rien de tout cela ne s'applique si la chaîne est dans une variable comme à partir d'une <input>balise HTML. var re = new RegExp("\"\\w+\"");est un exemple d'une expression rationnelle codée en dur utilisant le constructeur RegExp et l'échappement des guillemets doubles est nécessaire. Ce que je veux dire par une chaîne dans une variable, c'est que vous pouvez simplement le faire var re = new RegExp(str);et strpeut contenir des guillemets doubles ou des barres obliques inversées sans problème.
Luis Paulo

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

ou

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

Vous devez considérer qu'une entrée non valide comme /\/est reconnue.
Gumbo

8
Ou laissez le constructeur RegExp échouer, "trailing \ in regular expression", au lieu d'écrire un analyseur compliqué.
Anonyme

21

Voici un one-liner: str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')

Je l'ai obtenu du module NPM escape-string-regexp .

L'essayer:

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

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

Utilisation de littéraux de modèle balisés avec prise en charge des indicateurs:

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

Dans mon cas, l'entrée utilisateur était parfois entourée de délimiteurs et parfois non. j'ai donc ajouté un autre cas ..

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
vous pouvez toujours utiliser la .split()fonction au lieu d'une longue chaîne d'expression régulière. regParts = inputstring.split('/')cela ferait regParts[1]la chaîne d'expression régulière et regParts[2]les délimiteurs (en supposant que la configuration de l'expression régulière est /.../gim). Vous pouvez vérifier s'il y a des délimiteurs avec regParts[2].length < 0.
Jaketr00

3

Je vous suggère également d'ajouter des cases à cocher distinctes ou un champ de texte pour les drapeaux spéciaux. De cette façon, il est clair que l'utilisateur n'a pas besoin d'en ajouter //. Dans le cas d'un remplacement, fournissez deux champs de texte. Cela vous facilitera beaucoup la vie.

Pourquoi? Parce que sinon, certains utilisateurs ajouteront des //'tandis que d'autres ne le feront pas. Et certains feront une erreur de syntaxe. Ensuite, après avoir supprimé le //'s', vous pouvez vous retrouver avec une expression rationnelle syntaxiquement valide qui ne ressemble en rien à ce que l'utilisateur avait l'intention, conduisant à un comportement étrange (du point de vue de l'utilisateur).


2

Cela fonctionnera également lorsque la chaîne n'est pas valide ou ne contient pas d'indicateurs, 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 vous voulez vraiment convertir une chaîne en expression régulière, essayez d'utiliser la fonction suivante:

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

Vous pouvez l'utiliser comme ceci:

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

Pour référence, voici la version formatée et plus moderne:

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

Grâce aux réponses précédentes, ce bloc sert bien de solution générale pour appliquer une chaîne configurable dans un RegEx .. pour filtrer le texte:

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

Vous pouvez demander des indicateurs à l'aide de cases à cocher, puis faire quelque chose comme ceci:

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

on dirait que RegEx manque p de fin .. Stack ne me laisserait pas faire un montage à 1 caractère
Gene Bo

-3

J'utilise evalpour résoudre ce problème.

Par exemple:

    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 sur userInput est un risque fou pour la sécurité
Samuel Faure

1
tables mr bobby!
Luiz Felipe
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.