Leçon 6/6 11 min

Regex en JavaScript pour de vrai

test, match, matchAll, replace, les flags g i m s u. La leçon d'humilité de l'email, et les deux interdits : parser du HTML, et les motifs qui explosent.

Sortir du labo, entrer dans ton code

Depuis cinq leçons, tout tournait dans le labo. Tu tapais un motif, tu voyais le surlignage en direct, tu cherchais le vert. Pratique pour apprendre. Mais ton vrai code n'a pas de labo intégré. Il a des fonctions, des chaînes, des conditions.

Bonne nouvelle : tout ce que tu as appris se branche sur JavaScript avec très peu de méthodes. Quatre suffisent pour 99 % des cas. Le cours JavaScript est un prérequis ici, donc on ne réexplique pas les bases du langage. On montre juste où poser tes regex.

Cette leçon est le capstone du cours. On câble les quatre méthodes, on récapitule les flags, on attaque deux cas réels, puis vient la leçon d'humilité : l'email. Et enfin, les deux interdits qui séparent celui qui sait de celui qui se fait avoir.

Quatre méthodes, et c'est tout

Une regex en JavaScript s'écrit entre deux slashs : /motif/flags. Ensuite, tu la passes à l'une de ces quatre méthodes.

re.test(str) : vrai ou faux

La plus simple. Elle répond « est-ce que ce motif apparaît dans la chaîne ? » par un booléen. Parfaite pour valider une saisie.

const estPseudo = /^[a-z0-9_]{3,16}$/;
estPseudo.test('sam_42');  // true
estPseudo.test('Sam!');    // false

str.match(re) : le match, ou tous avec g

Sans le flag g, match rend le premier match et ses détails. Avec g, il rend un tableau de tous les matchs.

'#regex et #javascript'.match(/#\w+/);   // ['#regex', ...]
'#regex et #javascript'.match(/#\w+/g);  // ['#regex', '#javascript']

str.matchAll(re) : tous les matchs, avec leurs groupes

Quand tu veux tous les matchs et leurs groupes de capture (leçon 5), matchAll est l'outil. Il renvoie un itérateur, qu'on déroule avec le spread [...].

const texte = '2026-06-04 puis 2025-01-12';
for (const m of texte.matchAll(/(\d{4})-(\d{2})-(\d{2})/g)) {
  console.log(m[1], m[2], m[3]);  // '2026' '06' '04', puis '2025' '01' '12'
}
// ou en une fois : const tous = [...texte.matchAll(/(\d{4})-(\d{2})-(\d{2})/g)];

Le piège de matchAll : il exige le flag g. Sans lui, JavaScript lève une TypeError : « matchAll must be called with a global RegExp ». C'est volontaire : matchAll n'a de sens que pour parcourir tous les matchs. Oublie le g et ton code plante d'un coup, ce qui est plus sain qu'un bug silencieux.

str.replace(re, ...) et str.replaceAll(re, ...) : réécrire

Tu connais déjà replace depuis la leçon 5 : il réécrit en réutilisant les captures avec $1, $2. Avec un motif sans g, il remplace une fois ; avec g, partout.

'2026-06-04'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3/$2/$1');  // '04/06/2026'

Les flags, récapitulés

Les lettres après le second slash modifient le comportement du moteur. Tu en as déjà croisé plusieurs ; voici le tableau complet à garder sous la main.

FlagEffet
gTous les matchs, pas seulement le premier.
iInsensible à la casse : A et a sont équivalents.
mMultiligne (leçon 4) : ^ et $ matchent à chaque ligne.
sLe point . matche aussi le saut de ligne \n.
uMode Unicode. Rappel leçon 2 : \w reste ASCII même avec u ; le flag sert surtout aux caractères hors du plan de base.

On combine les flags librement : /erreur/gi trouve « erreur », « Erreur » et « ERREUR » partout dans le texte.

Cas réel n°1 : valider un pseudo

Un formulaire d'inscription. Tu veux un pseudo de 3 à 16 caractères, uniquement des lettres minuscules, des chiffres et des underscores. Tout le cours tient en une ligne.

function pseudoValide(s) {
  return /^[a-z0-9_]{3,16}$/.test(s);
}

Décortique le motif, leçon par leçon. Les ancres ^ et $ (leçon 4) imposent que la chaîne entière respecte la règle, du début à la fin. La classe [a-z0-9_] (leçon 2) liste les caractères autorisés. Le quantificateur {3,16} (leçon 3) fixe la longueur. Cinq leçons dans une seule expression.

Règle d'or de la validation : ancre toujours tes motifs de validation avec ^ et $. Sans eux, /[a-z0-9_]{3,16}/.test('sam!!!') renvoie true, parce que le moteur trouve « sam » au début, et ignore les trois points d'exclamation. Les ancres forcent le motif à juger la chaîne complète, pas un bout qui se cache dedans.

À toi de le manipuler. Le motif est pré-rempli : observe quelles chaînes passent au vert, et pourquoi les autres échouent.

🎯 Labo regex · valider un pseudo
//
Voir la solution

Le motif ^[a-z0-9_]{3,16}$ matche sam_42 (6 caractères autorisés). Il rejette Sam (le S majuscule n'est pas dans [a-z]), x (un seul caractère, en dessous du minimum de 3) et sam!42 (le ! n'est pas autorisé). Pour accepter les majuscules, tu ajouterais le flag i ou la plage A-Z dans la classe.

Cas réel n°2 : extraire les hashtags

Un champ de message. Tu veux récupérer tous les #hashtags pour les indexer. Le motif /#\w+/ dit : un croisillon # suivi d'un ou plusieurs caractères de mot. Avec le flag g, on les attrape tous.

const message = 'on lance #regex et #javascript demain';
const tags = message.match(/#\w+/g);  // ['#regex', '#javascript']

Dans le labo, mets g dans le champ des flags : les deux hashtags se surlignent en même temps. Sans le g, seul le premier serait pris.

🎯 Labo regex · extraire les hashtags
//
Voir la solution

Avec le flag g, #\w+ surligne #regex et #javascript dans la même chaîne. Le # est un littéral, \w+ capture la suite de lettres et chiffres. Retire le g et tu ne verras plus que le premier match : c'est exactement la différence entre match(re) et match(re) avec g.

La leçon d'humilité : l'email

Voici le piège dans lequel tombent presque tous les débutants : croire qu'il existe une regex « parfaite » pour valider un email. Elle n'existe pas. La norme officielle de l'email accepte des adresses si tordues que la regex qui les couvrirait toutes ferait des centaines de caractères, illisible et inutile.

Pire : même la regex parfaite ne te dirait pas si l'adresse est valide. existe-pas@nimporte-quoi.fr a une forme correcte, mais la boîte n'existe peut-être pas. On ne sait jamais si un email fonctionne tant qu'on n'a pas envoyé un mail dedans.

Le motif pratique, celui que tu utilises vraiment, couvre l'essentiel sans prétendre tout valider :

const formeEmail = /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/i;
formeEmail.test('sam@biblio.fr');  // true

Pour l'email : la regex valide la forme (« ça ressemble à un email »), pas l'adresse. La seule vraie validation, c'est d'envoyer un mail de confirmation avec un lien à cliquer. La forme filtre les fautes de frappe grossières côté navigateur ; le mail de confirmation prouve que l'adresse existe et appartient bien à l'utilisateur. Les deux ensemble, jamais la regex seule.

Joue le labo. Le motif accepte les adresses bien formées et rejette celles à qui il manque un morceau.

🎯 Labo regex · la forme d'un email
//
Voir la solution

Le motif accepte sam@biblio.fr et sam.lou@mairie-dijon.fr : une partie locale, un @, un domaine, un point, une extension d'au moins deux lettres. Il rejette sam@biblio (pas d'extension après un point) et @biblio.fr (partie locale vide avant le @). Le \. échappé exige un vrai point ; le {2,} impose au moins deux lettres d'extension.

Les deux interdits

Deux usages de regex tournent mal assez souvent pour qu'on les nomme clairement. Apprends à les reconnaître, et tu éviteras des heures de galère.

Interdit n°1 : parser du HTML avec une regex. Le HTML est imbriqué : une balise peut en contenir une autre, qui en contient une autre. Une regex ne sait pas compter les niveaux d'imbrication, donc elle se trompe dès que le HTML est un peu profond. Pour lire ou modifier du HTML, utilise le DOM (cours JavaScript, leçon DOM) : document.querySelector et compagnie comprennent la structure, là où la regex ne voit qu'une suite de caractères.

Interdit n°2 : les quantificateurs imbriqués (ReDoS). Un motif comme (a+)+ imbrique un quantificateur dans un autre. Sur une entrée hostile, le moteur explore un nombre astronomique de combinaisons, et le temps de calcul explose : c'est le catastrophic backtracking, une porte d'entrée pour un déni de service. Le réflexe est simple : garde tes motifs droits, sans imbriquer un + ou un * dans un groupe lui-même répété. La culture déni de service est dans le cours Sécurité.

Pour aller plus loin : il existe aussi le lookahead, écrit (?=...), qui regarde devant sans consommer les caractères. C'est hors du périmètre de ce cours, mais maintenant tu sauras le reconnaître quand tu le croiseras dans le motif de quelqu'un d'autre.

Tes six réflexes regex

Six leçons, six réflexes. C'est le bagage qui te sert dans 99 % des cas réels.

  • Leçon 1 : une regex décrit un motif ; le point . matche presque tout, et un vrai point s'écrit \..
  • Leçon 2 : les classes [...] et les raccourcis \d \w \s choisissent UN caractère parmi un ensemble.
  • Leçon 3 : les quantificateurs * + ? {n,m} répètent ; méfie-toi du .* gourmand.
  • Leçon 4 : les ancres ^ $ et la frontière \b situent le motif dans le texte.
  • Leçon 5 : les groupes (...) capturent, $1 réutilise, | offre des choix.
  • Leçon 6 : en JavaScript, test, match, matchAll, replace ; valide la forme, jamais plus.

Côté outillage : quand un motif complexe te résiste, ne reste pas à le fixer. Colle-le dans regex101.com : l'outil le découpe morceau par morceau et explique en français ce que fait chaque bout. C'est le meilleur débogueur de regex, et il t'apprend en même temps qu'il te dépanne.

Leaving the lab, entering your code

For five lessons, everything ran in the lab. You typed a pattern, watched the live highlighting, hunted for green. Handy for learning. But your real code has no built-in lab. It has functions, strings, conditions.

Good news: everything you learned plugs into JavaScript with very few methods. Four cover 99% of cases. The JavaScript course is a prerequisite here, so we won't re-explain the language basics. We'll just show where to place your regexes.

This lesson is the course capstone. We wire the four methods, recap the flags, tackle two real cases, then comes the humility lesson: email. And finally, the two don'ts that separate the one who knows from the one who gets fooled.

Four methods, and that's it

A regex in JavaScript is written between two slashes: /pattern/flags. Then you pass it to one of these four methods.

re.test(str): true or false

The simplest one. It answers "does this pattern appear in the string?" with a boolean. Perfect for validating input.

const isUsername = /^[a-z0-9_]{3,16}$/;
isUsername.test('sam_42');  // true
isUsername.test('Sam!');    // false

str.match(re): the match, or all of them with g

Without the g flag, match returns the first match and its details. With g, it returns an array of every match.

'#regex and #javascript'.match(/#\w+/);   // ['#regex', ...]
'#regex and #javascript'.match(/#\w+/g);  // ['#regex', '#javascript']

str.matchAll(re): all matches, with their groups

When you want every match and its capture groups (lesson 5), matchAll is the tool. It returns an iterator, which you unroll with the spread [...].

const text = '2026-06-04 then 2025-01-12';
for (const m of text.matchAll(/(\d{4})-(\d{2})-(\d{2})/g)) {
  console.log(m[1], m[2], m[3]);  // '2026' '06' '04', then '2025' '01' '12'
}
// or all at once: const all = [...text.matchAll(/(\d{4})-(\d{2})-(\d{2})/g)];

Beware, the matchAll trap: it requires the g flag. Without it, JavaScript throws a TypeError: "matchAll must be called with a global RegExp". It's deliberate: matchAll only makes sense to walk through all matches. Forget the g and your code crashes at once, which is healthier than a silent bug.

str.replace(re, ...) and str.replaceAll(re, ...): rewriting

You already know replace from lesson 5: it rewrites by reusing captures with $1, $2. With a pattern without g, it replaces once; with g, everywhere.

'2026-06-04'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3/$2/$1');  // '04/06/2026'

The flags, recapped

The letters after the second slash change the engine's behaviour. You've met several already; here is the full table to keep handy.

FlagEffect
gAll matches, not just the first.
iCase-insensitive: A and a are equivalent.
mMultiline (lesson 4): ^ and $ match on every line.
sThe dot . also matches the line break \n.
uUnicode mode. Recall lesson 2: \w stays ASCII even with u; the flag mainly helps with characters beyond the basic plane.

Flags combine freely: /error/gi finds "error", "Error" and "ERROR" everywhere in the text.

Real case #1: validate a username

A sign-up form. You want a username of 3 to 16 characters, only lowercase letters, digits and underscores. The whole course fits in one line.

function validUsername(s) {
  return /^[a-z0-9_]{3,16}$/.test(s);
}

Break the pattern down, lesson by lesson. The anchors ^ and $ (lesson 4) force the entire string to obey the rule, start to end. The class [a-z0-9_] (lesson 2) lists the allowed characters. The quantifier {3,16} (lesson 3) sets the length. Five lessons in one expression.

Golden rule of validation: always anchor your validation patterns with ^ and $. Without them, /[a-z0-9_]{3,16}/.test('sam!!!') returns true, because the engine finds "sam" at the start and ignores the three exclamation marks. The anchors force the pattern to judge the whole string, not a slice hiding inside it.

Your turn to play with it. The pattern is pre-filled: watch which strings go green, and why the others fail.

🎯 Regex lab · validate a username
//
Show the solution

The pattern ^[a-z0-9_]{3,16}$ matches sam_42 (6 allowed characters). It rejects Sam (the uppercase S is not in [a-z]), x (a single character, below the minimum of 3) and sam!42 (the ! is not allowed). To accept uppercase, you would add the i flag or the A-Z range to the class.

Real case #2: extract the hashtags

A message field. You want to grab every #hashtag to index it. The pattern /#\w+/ says: a hash # followed by one or more word characters. With the g flag, we catch them all.

const message = 'we launch #regex and #javascript tomorrow';
const tags = message.match(/#\w+/g);  // ['#regex', '#javascript']

In the lab, put g in the flags field: both hashtags highlight at once. Without the g, only the first would be taken.

🎯 Regex lab · extract the hashtags
//
Show the solution

With the g flag, #\w+ highlights #regex and #javascript in the same string. The # is a literal, \w+ captures the run of letters and digits. Remove the g and you'll only see the first match: that's exactly the difference between match(re) and match(re) with g.

The humility lesson: email

Here's the trap almost every beginner falls into: believing there is a "perfect" regex to validate an email. There isn't. The official email standard accepts addresses so twisted that the regex covering them all would be hundreds of characters long, unreadable and useless.

Worse: even the perfect regex wouldn't tell you whether the address is valid. does-not-exist@whatever.com has a correct shape, but the mailbox may not exist. You never know whether an email works until you've sent a mail to it.

The practical pattern, the one you actually use, covers the essentials without pretending to validate everything:

const emailShape = /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/i;
emailShape.test('sam@library.com');  // true

Email best practice: the regex validates the shape ("it looks like an email"), not the address. The only real validation is to send a confirmation email with a link to click. The shape filters gross typos browser-side; the confirmation email proves the address exists and truly belongs to the user. The two together, never the regex alone.

Play the lab. The pattern accepts well-formed addresses and rejects those missing a piece.

🎯 Regex lab · the shape of an email
//
Show the solution

The pattern accepts sam@biblio.fr and sam.lou@mairie-dijon.fr: a local part, an @, a domain, a dot, an extension of at least two letters. It rejects sam@biblio (no extension after a dot) and @biblio.fr (empty local part before the @). The escaped \. demands a real dot; the {2,} requires at least two extension letters.

The two don'ts

Two regex uses go wrong often enough to be named clearly. Learn to recognise them, and you'll save yourself hours of pain.

Don't #1: parsing HTML with a regex. HTML is nested: a tag can contain another, which contains another. A regex can't count nesting levels, so it gets it wrong as soon as the HTML goes a little deep. To read or modify HTML, use the DOM (JavaScript course, DOM lesson): document.querySelector and friends understand the structure, where the regex only sees a run of characters.

Don't #2: nested quantifiers (ReDoS). A pattern like (a+)+ nests one quantifier inside another. On a hostile input, the engine explores an astronomical number of combinations, and compute time explodes: that's catastrophic backtracking, a doorway to a denial of service. The reflex is simple: keep your patterns flat, without nesting a + or a * inside a group that is itself repeated. The denial-of-service culture is in the Security course.

To go further: there is also the lookahead, written (?=...), which looks ahead without consuming the characters. It's out of scope for this course, but now you'll recognise it when you meet it in someone else's pattern.

Your six regex reflexes

Six lessons, six reflexes. That's the toolkit that serves you in 99% of real cases.

  • Lesson 1: a regex describes a pattern; the dot . matches almost anything, and a real dot is written \..
  • Lesson 2: the classes [...] and the shortcuts \d \w \s pick ONE character from a set.
  • Lesson 3: the quantifiers * + ? {n,m} repeat; beware the greedy .*.
  • Lesson 4: the anchors ^ $ and the boundary \b place the pattern in the text.
  • Lesson 5: the groups (...) capture, $1 reuses, | offers choices.
  • Lesson 6: in JavaScript, test, match, matchAll, replace; validate the shape, never more.

Tooling tip: when a complex pattern resists you, don't sit there staring at it. Paste it into regex101.com: the tool slices it piece by piece and explains in plain words what each bit does. It's the best regex debugger, and it teaches you while it rescues you.

🎯 Pratique

S'entraîner (clique pour ouvrir) :

💬 Ré-explique sans regarder
Ré-explique sans regarder

Explique à un collègue les quatre méthodes regex de JavaScript (test, match, matchAll, replace) : à quoi sert chacune, et laquelle exige le flag g.

Une bonne explication dit : test renvoie un booléen (le motif apparaît, oui ou non), idéal pour valider. match renvoie le premier match, ou tous avec le flag g. matchAll renvoie tous les matchs avec leurs groupes, et exige le flag g (sinon TypeError) ; on le déroule avec le spread [...]. replace (et replaceAll) réécrit en réutilisant les captures $1.
🧠 Rappel libre
Rappel libre

Sans remonter : pourquoi une regex ne peut pas vraiment valider un email, et que faut-il faire en plus ?

La regex valide seulement la forme (« ça ressemble à un email »). La norme accepte des adresses si tordues qu'aucune regex raisonnable ne les couvre toutes, et surtout une forme correcte ne prouve pas que la boîte existe. La seule vraie validation : envoyer un mail de confirmation avec un lien à cliquer. Forme côté navigateur + confirmation par mail, jamais la regex seule.
⚖️ Juge le code de l'IA
Accepter ou rejeter le code de l'IA

Tu demandes à l'IA de valider un email. Elle te livre fièrement « la regex RFC complète » : un monstre de 400 caractères copié d'une réponse Stack Overflow, qu'elle dit « 100 % conforme à la norme ». Tu acceptes, ou tu rejettes ?

À rejeter. La regex RFC de 400 caractères est illisible (impossible à relire ou maintenir) et inutile : même conforme, elle ne prouve pas que la boîte existe. Le bon réflexe est la forme pratique /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/i pour filtrer les fautes de frappe, PLUS un mail de confirmation pour valider l'adresse. La complexité maximale n'est pas la qualité maximale.
Tu veux récupérer TOUS les matchs avec leurs groupes de capture. Quelle méthode, et quelle contrainte ?
Quelle est la bonne approche pour « valider » un email côté inscription ?
Pourquoi ne faut-il pas parser du HTML avec une regex ?
Synthèse du cours. Lis ce motif : /^(\d{2})\/(\d{2})$/. Que valide-t-il exactement ?
Prochaine étape

Tu sais maintenant valider la forme des entrées avec une regex. Mais valider la forme ne suffit pas : le cours Sécurité t'apprend pourquoi on valide TOUJOURS côté serveur, et ce qui arrive quand on ne le fait pas.

Cours : Sécurité web →

Une erreur dans cette leçon, un passage flou, une question ? Écrivez-moi : chaque retour améliore ce cours.

Besoin d'un développeur pour votre projet ?

Réponse sous 24h · Sans engagement