kalvn logo kalvn

L'essentiel sur les expressions régulières en JavaScript

Les expressions régulières sont la bête noire d'une majorité de développeurs. Cryptiques et difficiles à aborder, mais tellement puissantes une fois maîtrisées.

En ce qui me concerne, je les ai complètement mises de côté au début de mon apprentissage pour finalement avoir un déclic lorsque je me suis retrouvé à mettre en place des stratagèmes invraisemblables simplement pour éviter d'avoir à m'y confronter.

Par contre l'une des difficultés à laquelle je me heurte toujours aujourd'hui, c'est que la façon d'utiliser les expressions régulières varient beaucoup d'un language à l'autre. Il n'y a pas vraiment de consensus.

L'objectif de cet article est donc de regrouper tout ce qu'il y a à savoir pour utiliser les expression régulières — ou regex — en JavaScript. Je vais m'attarder principalement sur les outils plus que sur la syntaxe elle-même, pour laquelle vous trouverez bien des ressources sur le web.

Déclarer une regex

La déclaration d'une expression régulière est assez similaire d'un language à l'autre, avec quelques subtilités. PHP s'attend par exemple à ce que la regex soit déclarée sous forme de string, là où JavaScript permet de se passer des quotes dès lors qu'on utilise la syntaxe avec les 2 slashs /.

Entrons dans le vif du sujet.

JavaScript permet de déclarer une regex de 2 façons :

// Méthode 1 : entre 2 slashs, avec les flags à la fin.
const emailAddressPatternWithSlashes = /^.*@.*\..*$/i;

// Méthode 2 : en utilisant l'objet RegExp.
const emailAddressPatternWithClass = new RegExp('^.*@.*\\..*$', 'i');

La première méthode est plus concise et permet de ne pas avoir à échapper les single quotes ', mais elle ne permet pas de construire l'expression dynamiquement.

La seconde méthode est plus verbeuse mais plus explicite, et elle rend les flags plus visibles. Par contre attention à bien échapper les caractères spéciaux avec deux backslashes \\ au lieu d'un seul : un pour la chaîne JavaScript, et un pour la regex.

Ces deux méthodes sont valables. Je tombe plus souvent sur la première qui convient parfaitement pour des patterns courts, là où la seconde convient mieux à des cas complexes.

Les différentes méthodes

Intéressons nous à présent aux différentes fonctions disponibles pour vérifier si une chaîne de caractères est valide pour une regex donnée. Pour ce faire, je vais utiliser l'exemple suivant tout au long de l'article, qui correspond à la recherche d'un numéro de téléphone :

const pattern = '([\\d+][\\d \\-()+]{5,}[\\d])';
const regex = new RegExp(pattern);
const text = 'Mon numéro est le 06 39 12 34 56, ou en notation internationale : +33-6 39 12 34 56';

String.match(RegExp)

Cette méthode retourne un tableau si une correspondance est trouvée, et null dans le cas contraire.

Le contenu du tableau dépend de la présence ou non du flag g — global.

text.match(pattern);

retourne :

[
	0: "06 39 12 34 56"
	1: "06 39 12 34 56"
	groups: undefined
	index: 18
	input: "Mon numéro est le 06 39 12 34 56, ou en notation internationale : +33-6 39 12 34 56"
	length: 2
]

On voit bien que seule la première correspondance est retournée. La propriété index: 18 correspond à son indice.

Si on ajoute le flag g :

text.match(new RegExp(pattern, 'g'));

Le résultat est différent et contient simplement les correspondances complètes :

[
	0: "06 39 12 34 56"
	1: "+33-6 39 12 34 56"
	length: 2
]

Cette méthode retourne beaucoup d'informations sur les différents groupes ou correspondances. Cependant, c'est celle qui requiert une meilleure connaissance de la documentation dans la mesure où le résultat n'est pas nécessairement intuitif.

String.search(RegExp)

Plus simple que la méthode précédente, search() retourne simplement l'indice de la première correspondance ou -1 s'il n'y en a aucune.

text.search(regex); // = 18

Parfaite pour les cas simples où vous souhaitez simplement trouver la position de la première correspondance.

RegExp.test(String)

Cette méthode — qui s'applique cette fois à une RegExp et qui accepte une chaîne de caractères en paramètre — retourne simplement true ou false selon que le paramètre a une correspondance avec l'expression régulière ou pas.

regex.test(text); // = true

La petite subtilité ici est que, si vous utilisez le flag g, l'instance de RegExp devient stateful, ce qui signifie qu'elle mémorise des informations liées à la dernière évaluation.

Exécuter plusieurs fois la méthode test() d'affilé ne donnera donc pas toujours le même résultat.

const globalRegex = new RegExp(pattern, 'g');

globalRegex.test(text); // = true, correspond à 06 39 12 34 56
globalRegex.test(text); // = true, correspond à +33-6 39 12 34 56
globalRegex.test(text); // = false, plus de correspondance

À utiliser pour simplement vérifier si une chaîne de caractères correspond à un pattern donné.

RegExp.exec(String)

Cette dernière méthode est l'exact inverse de String.match(RegExp) décrite plus haut, c'est à dire qu'elle retourne un tableau dont le contenu dépend de la présence ou non du flag g, et null s'il n'y a pas de correspondance.

regex.exec(text);

retourne :

[
	0: "06 39 12 34 56"
	1: "06 39 12 34 56"
	groups: undefined
	index: 18
	input: "Mon numéro est le 06 39 12 34 56, ou en notation internationale : +33-6 39 12 34 56"
	length: 2
]

La différence est que si le flag g est utilisé, vous pouvez alors l'exécuter plusieurs fois pour itérer sur les correspondances :

const globalRegex = new RegExp(pattern, 'g');

globalRegex.exec(text); // = même résultat que ci-dessus
globalRegex.exec(text); // =
// [
//   0: "+33-6 39 12 34 56"
//   1: "+33-6 39 12 34 56"
//   groups: undefined
//   index: 66
//   input: "Mon numéro est le 06 39 12 34 56, ou en notation internationale : +33-6 39 12 34 56"
//   length: 2
// ]

Similaire à String.match mais avec quelques options supplémentaires. Attention à l'utilisation du flag g en cas d'exécutions successives.

Pensez à échapper les caractères spéciaux

Notamment lorsque vous utilisez une information qui vient de l'extérieur au sein d'une expression régulière, il est essentiel d'échapper tous les caractères spéciaux propres à la syntaxe des regex.

Pour ce faire, une nouvelle méthode a fait son apparition en 2025 : RegExp.escape().

Elle permet de transformer tous les caractères spéciaux des expressions régulières (^, $, \, ., *, +, ?, (, ), [, ], {, }, et |) en leur équivalent unicode :

RegExp.escape("Buy it. use it. break it. fix it.");
// "\\x42uy\\x20it\\.\\x20use\\x20it\\.\\x20break\\x20it\\.\\x20fix\\x20it\\."

Utilisez les bons outils

Lorsqu'il s'agit de créer un pattern de regex, l'intelligence artificielle ne se débrouille en général pas trop mal. Ça m'est arrivé néanmoins de la voir halluciner et proposer un pattern qui ne correspond pas du tout à ce qui était demandé.

C'est pourquoi j'ai besoin d'un outil déterministe pour valider mes regex complexes, et l'outil que j'utilise depuis des années c'est regular expressions 101 qui est très complet, comprend la syntaxe de plusieurs languages, et propose des explications détaillées sur l'interprétation de vos regex.

Il offre en outre un catalogue de patterns proposés par la communauté pour vous aider avec les cas les plus communs.

Pour approfondir