Existe-t-il une fonction RegExp.escape en Javascript?

443

Je veux juste créer une expression régulière à partir de n'importe quelle chaîne possible.

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

Existe-t-il une méthode intégrée pour cela? Sinon, qu'utilisent les gens? Ruby l'a fait RegExp.escape. Je n'ai pas l'impression d'avoir besoin d'écrire le mien, il doit y avoir quelque chose de standard. Merci!

Lance Pollard
la source
15
Je voulais juste vous mettre à jour des gens raffinés sur lesquels RegExp.escapeon travaille actuellement et toute personne qui pense avoir une contribution précieuse est la bienvenue. core-js et autres polyfills le proposent.
Benjamin Gruenbaum
5
Selon la récente mise à jour de cette réponse, cette proposition a été rejetée: Voir le problème
try-catch-finally

Réponses:

574

La fonction liée ci-dessus est insuffisante. Il ne parvient pas à s'échapper ^ou $(début et fin de la chaîne), ou -, qui dans un groupe de caractères est utilisé pour les plages.

Utilisez cette fonction:

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

Bien que cela puisse sembler inutile à première vue, l'échappement -(ainsi que ^) rend la fonction appropriée pour les caractères d'échappement à insérer dans une classe de caractères ainsi que le corps de l'expression régulière.

L'échappement /rend la fonction appropriée pour les caractères d'échappement à utiliser dans un littéral d'expression régulière JS pour une évaluation ultérieure.

Comme il n'y a aucun inconvénient à échapper à l'un ou l'autre, il est logique de s'échapper pour couvrir des cas d'utilisation plus larges.

Et oui, c'est décevant à défaut que cela ne fasse pas partie du JavaScript standard.

bobince
la source
16
En fait, on n'a pas besoin d'échapper /à tout
THORN
28
@Paul: Perl quotemeta( \Q), Python re.escape, PHP preg_quote, Ruby Regexp.quote...
bobince
13
Si vous allez utiliser cette fonction dans une boucle, il est probablement préférable de faire de l'objet RegExp sa propre variable var e = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g;, puis votre fonction est de return s.replace(e, '\\$&');cette façon, vous instanciez le RegExp une seule fois.
styfle
15
Les arguments standard contre l'augmentation des objets intégrés s'appliquent ici, non? Que se passe-t-il si une future version d'ECMAScript fournit un RegExp.escapedont l'implémentation diffère de la vôtre? Ne serait-il pas préférable que cette fonction ne soit attachée à rien?
Mark Amery
15
bobince ne se soucie pas de l'opinion d'
eslint
115

Pour toute personne utilisant lodash, depuis la v3.0.0, une fonction _.escapeRegExp est intégrée:

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

Et, dans le cas où vous ne souhaitez pas avoir besoin de la bibliothèque lodash complète, vous pouvez avoir besoin de cette fonction !

Gustavohenke
la source
6
il y a même un package npm de tout cela! npmjs.com/package/lodash.escaperegexp
Ted Pennings
1
Cela importe des charges de code qui n'ont vraiment pas besoin d'être là pour une chose aussi simple. Utilisez la réponse de bobince ... ça marche pour moi et c'est tellement moins d'octets à charger que la version lodash!
Rob Evans
6
@RobEvans ma réponse commence par "Pour tous ceux qui utilisent lodash" , et je mentionne même que vous ne pouvez exiger que la escapeRegExpfonction.
gustavohenke
2
@gustavohenke Désolé, j'aurais dû être un peu plus clair, j'ai inclus le module lié à votre "juste cette fonction" et c'est ce que je commentais. Si vous jetez un coup d'œil, il y a beaucoup de code pour ce qui devrait être une seule fonction avec une seule expression rationnelle. D'accord si vous utilisez déjà lodash, alors il est logique de l'utiliser, mais sinon, utilisez l'autre réponse. Désolé pour le commentaire peu clair.
Rob Evans
2
@maddob Je ne vois pas que \ x3 vous l'avez mentionné: mes chaînes d'échappement semblent bonnes, exactement ce que j'attends
Federico Fissore
43

La plupart des expressions ici résolvent des cas d'utilisation spécifiques uniques.

Ce n'est pas grave, mais je préfère une approche "toujours efficace".

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

Cela "échappera complètement" à une chaîne littérale pour l'une des utilisations suivantes dans les expressions régulières:

  • Insertion dans une expression régulière. Par exemplenew RegExp(regExpEscape(str))
  • Insertion dans une classe de caractères. Par exemplenew RegExp('[' + regExpEscape(str) + ']')
  • Insertion dans le spécificateur de nombre entier. Par exemplenew RegExp('x{1,' + regExpEscape(str) + '}')
  • Exécution dans des moteurs d'expression régulière non JavaScript.

Caractères spéciaux couverts:

  • -: Crée une plage de caractères dans une classe de caractères.
  • [/ ]: Démarre / termine une classe de caractères.
  • {/ }: Démarre / termine un spécificateur de numérotation.
  • (/ ): Démarre / termine un groupe.
  • */ +/ ?: Spécifie le type de répétition.
  • .: Correspond à n'importe quel caractère.
  • \: Échappe les caractères et démarre les entités.
  • ^: Spécifie le début de la zone de correspondance et annule la correspondance dans une classe de caractères.
  • $: Spécifie la fin de la zone correspondante.
  • |: Spécifie l'alternance.
  • #: Spécifie le commentaire en mode d'espacement libre.
  • \s: Ignoré en mode d'espacement libre.
  • ,: Sépare les valeurs dans le spécificateur de numération.
  • /: Démarre ou termine l'expression.
  • :: Complète les types de groupes spéciaux et fait partie des classes de caractères de style Perl.
  • !: Annule le groupe de largeur nulle.
  • </ =: Fait partie des spécifications de groupe de largeur nulle.

Remarques:

  • /n'est strictement nécessaire dans aucune saveur d'expression régulière. Cependant, il protège au cas où quelqu'un (frisson) fait eval("/" + pattern + "/");.
  • , garantit que si la chaîne est censée être un entier dans le spécificateur numérique, elle provoquera correctement une erreur de compilation RegExp au lieu de se tromper silencieusement.
  • #et \sn'ont pas besoin d'être échappés en JavaScript, mais dans de nombreuses autres versions. Ils sont échappés ici au cas où l'expression régulière serait transmise ultérieurement à un autre programme.

Si vous avez également besoin de pérenniser l'expression régulière contre les ajouts potentiels aux capacités du moteur regex JavaScript, je recommande d'utiliser le plus paranoïaque:

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

Cette fonction échappe à tous les caractères sauf ceux explicitement garantis de ne pas être utilisés pour la syntaxe dans les futures versions d'expressions régulières.


Pour les véritables amateurs d'assainissement, considérons ce cas de pointe:

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

Cela devrait compiler correctement en JavaScript, mais ne le sera pas dans d'autres versions. Si vous avez l'intention de passer à une autre saveur, le cas nul de s === ''devrait être vérifié indépendamment, comme ceci:

var s = '';
new RegExp('(choice1|choice2' + (s ? '|' + regExpEscape(s) : '') + ')');
Pi Marillion
la source
1
Le /n'a pas besoin d'être échappé dans la [...]classe de caractère.
Dan Dascalescu
1
La plupart d'entre eux n'ont pas besoin d'être échappés. "Crée une plage de caractères dans une classe de caractères" - vous n'êtes jamais dans une classe de caractères à l'intérieur de la chaîne. "Spécifie le commentaire en mode d'espacement libre, ignoré en mode d'espacement libre" - non pris en charge en javascript. "Séparer les valeurs dans le spécificateur de numération" - vous n'êtes jamais dans le spécificateur de numération à l'intérieur de la chaîne. De plus, vous ne pouvez pas écrire de texte arbitraire dans la spécification de dénomination. "Démarre ou termine l'expression" - pas besoin de s'échapper. Eval n'est pas un cas, car il faudrait beaucoup plus de fuite. [se poursuivra dans le prochain commentaire]
Qwertiy
"Complète les types de groupes spéciaux et une partie des classes de caractères de style Perl" - ne semble pas disponible en javascript. "Nie le groupe de largeur nulle, partie des spécifications de groupe de largeur nulle" - vous n'avez jamais de groupes à l'intérieur de la chaîne.
Qwertiy
@Qwertiy La raison de ces évasions supplémentaires est d'éliminer les cas marginaux qui pourraient causer des problèmes dans certains cas d'utilisation. Par exemple, l'utilisateur de cette fonction peut vouloir insérer la chaîne d'expression régulière échappée dans une autre expression régulière dans le cadre d'un groupe, ou même pour une utilisation dans une autre langue que Javascript. La fonction ne fait pas d'hypothèses comme "Je ne ferai jamais partie d'une classe de caractères", car elle est censée être générale . Pour une approche plus YAGNI, voir l'une des autres réponses ici.
Pi Marillion
Très bien. Pourquoi _ n'est-il pas échappé? Qu'est-ce qui garantit qu'il ne deviendra probablement pas la syntaxe regex plus tard?
madprops
30

Le Guide des expressions régulières du Mozilla Developer Network fournit cette fonction d'échappement:

function escapeRegExp(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
menthe douce
la source
@DanDascalescu Vous avez raison. La page MDN a été mise à jour et =n'est plus incluse.
quietmint
21

Dans le widget de saisie semi-automatique de jQueryUI (version 1.9.1), ils utilisent une expression régulière légèrement différente (ligne 6753), voici l'expression régulière combinée à l'approche @bobince.

RegExp.escape = function( value ) {
     return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
}
Pierluc SS
la source
4
La seule différence est qu'ils s'échappent ,(qui n'est pas un métacaractère) #et les espaces blancs qui ne comptent qu'en mode d'espacement libre (qui n'est pas pris en charge par JavaScript). Cependant, ils ont raison de ne pas échapper à la barre oblique.
Martin Ender
18
Si vous souhaitez réutiliser l'implémentation de jquery UI plutôt que de coller le code localement, allez avec $.ui.autocomplete.escapeRegex(myString).
Scott Stafford
2
lodash a cela aussi, _. escapeRegExp et npmjs.com/package/lodash.escaperegexp
Ted Pennings
v1.12 pareil, ok!
Peter Krauss
13

Rien ne devrait vous empêcher de simplement échapper à chaque caractère non alphanumérique:

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

Vous perdez un certain degré de lisibilité en faisant re.toString() mais vous gagnez beaucoup de simplicité (et de sécurité).

Selon ECMA-262, d'une part, l' expression régulière « caractères de syntaxe » sont toujours non-alphanumériques, de sorte que le résultat est sécurisé, et des séquences d'échappement spéciales ( \d, \w, \n) sont toujours alphanumériques de telle sorte qu'aucun échappe de contrôle faux seront produits .

filip
la source
Simple et efficace. J'aime beaucoup mieux que la réponse acceptée. Pour (vraiment) les anciens navigateurs, .replace(/[^\w]/g, '\\$&')cela fonctionnerait de la même manière.
Tomas Langkaas
6
Cela échoue en mode Unicode. Par exemple, new RegExp('🍎'.replace(/(?=\W)/g, '\\'), 'u')lève une exception car \Wcorrespond à chaque unité de code d'une paire de substitution séparément, ce qui entraîne des codes d'échappement non valides.
Alexey Lebedev
1
alternative:.replace(/\W/g, "\\$&");
Miguel Pynto
@AlexeyLebedev La réponse a-t-elle été corrigée pour gérer le mode Unicode? Ou existe-t-il une solution ailleurs qui le fait, tout en conservant cette simplicité?
johny pourquoi
6

Il s'agit d'une version plus courte.

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

Cela inclut les caractères non-méta de %, &, 'et ,, mais la spécification RegExp JavaScript permet.

kzh
la source
2
Je n'utiliserais pas cette version "plus courte", car les plages de caractères masquent la liste des caractères, ce qui rend plus difficile de vérifier l'exactitude à première vue.
nhahtdh
@nhahtdh Je ne le ferais probablement pas non plus, mais il est affiché ici pour information.
kzh
@kzh: publier "pour information" aide moins que publier pour comprendre. N'êtes-vous pas d'accord pour dire que ma réponse est plus claire?
Dan Dascalescu
Au moins, .c'est raté. Et (). Ou pas? [-^est étrange. Je ne me souviens pas de ce qu'il y a.
Qwertiy
Ce sont dans la plage spécifiée.
kzh
3

Plutôt que d'échapper uniquement aux caractères qui causeront des problèmes dans votre expression régulière (par exemple: une liste noire), pourquoi ne pas envisager d'utiliser une liste blanche à la place. De cette façon, chaque personnage est considéré comme corrompu, sauf s'il correspond.

Pour cet exemple, supposez l'expression suivante:

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

Cette liste blanche lettres, chiffres et espaces:

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

Retour:

"be \|\| \! be"

Cela peut échapper à des personnages qui n'ont pas besoin d'être échappés, mais cela n'entrave pas votre expression (peut-être quelques pénalités de temps mineures - mais cela en vaut la peine pour la sécurité).

bashaus
la source
Est-ce différent de la réponse 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
la source
1

Les fonctions des autres réponses sont excessives pour échapper des expressions régulières entières (elles peuvent être utiles pour échapper des parties d'expressions régulières qui seront ensuite concaténées dans des expressions rationnelles plus grandes).

Si vous échappez un regexp entier et fini avec elle, citant les métacaractères qui sont soit autonomes ( ., ?, +, *, ^, $, |, \) ou commencer quelque chose ( (, [, {) est tout ce dont vous avez besoin:

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

Et oui, il est décevant que JavaScript n'ait pas de fonction comme celle-ci intégrée.

Dan Dascalescu
la source
Disons que vous échappez à l'entrée utilisateur (text)nextet l'insérez dans: (?:+ entrée + ). Votre méthode donnera la chaîne résultante (?:\(text)next)qui ne parvient pas à compiler. Notez que c'est une insertion tout à fait raisonnable, pas une folle comme re\+ entrée + re(dans ce cas, le programmeur peut être blâmé pour avoir fait quelque chose de stupide)
nhahtdh
1
@nhahtdh: ma réponse mentionnait spécifiquement échapper des expressions régulières entières et "en finir" avec elles, pas des parties (ou des parties futures) d'expressions rationnelles. Veuillez annuler le vote négatif?
Dan Dascalescu
Il est rare que vous échappiez à l'expression entière - il y a une opération de chaîne, qui est beaucoup plus rapide que regex si vous voulez travailler avec une chaîne littérale.
nhahtdh
Cela ne mentionne pas qu'il est incorrect - \devrait être échappé, car votre regex laissera \wintact. De plus, JavaScript ne semble pas autoriser la fuite ), du moins c'est pour cela que Firefox lance une erreur.
nhahtdh
1
Veuillez aborder la partie concernant la fermeture)
nhahtdh
1

Une autre approche (beaucoup plus sûre) consiste à échapper à tous les personnages (et pas seulement à quelques caractères spéciaux que nous connaissons actuellement) en utilisant le format d'échappement 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}'

Veuillez noter que vous devez passer le udrapeau pour que cette méthode fonctionne:

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

Il n'y a jamais eu et il n'y aura jamais que 12 méta caractères à échapper
pour être considérés comme un littéral.

Peu importe ce qui est fait avec la chaîne d'échappement, insérée dans un
wrapper regex , ajoutée, n'a pas d'importance.

Remplacez une chaîne en utilisant ceci

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

la source
qu'en est-il ]?
Thomasleveil