Skip to content
Retour au blog
Tutoriels

Aide-mémoire Regex : métacaractères, groupes et lookarounds

Aide-mémoire regex : métacaractères, quantificateurs, ancres, groupes, lookarounds, plus 15+ motifs JavaScript et Python et des correctifs pour le backtracking catastrophique.

12 min de lecture

Aide-mémoire regex : métacaractères, groupes et lookarounds (référence complète)

Une expression régulière est un mini-langage de motifs pour filtrer du texte. \d+ veut dire « un ou plusieurs chiffres », ^Error veut dire « une ligne qui commence par Error ». C’est à peu près tout. Cet aide-mémoire regroupe la syntaxe sur une seule page défilante : métacaractères, quantificateurs, ancres, groupes, lookarounds, flags, et 15 motifs prêts à coller dans JavaScript ou Python.

Il s’adresse aux développeurs qui savent déjà ce qu’est une chaîne et veulent une référence, pas une visite guidée. Sautez au tableau de référence rapide si vous cherchez juste les symboles. Lisez les sections sur les lookarounds et les pièges si une regex vous a déjà fait planter un serveur.

1. Qu’est-ce qu’une regex et pourquoi vous en avez encore besoin en 2026

Une regex est un motif compilé en automate à états qui parcourt une chaîne et soit correspond, soit échoue. La grammaire est petite, les cas d’usage ne le sont pas.

Une IA peut vous esquisser un motif, mais trois tâches restent à écrire à la main :

  • Analyse de logs : vous avez dix millions de lignes de logs d’accès nginx et vous voulez extraire chaque requête 5xx d’un user agent donné. Une regex de 40 caractères via grep -E s’exécute en quelques secondes ; un appel LLM par ligne, non.
  • Validation de formulaires : numéros de téléphone, codes postaux, timestamps ISO, clés de licence. Le motif vit à côté du champ et s’exécute à chaque frappe dans le navigateur.
  • Recherche-remplacement en masse : refactoriser mille fichiers en capturant un nom puis en le réinjectant. sed, ripgrep et la fonction « Remplacer dans les fichiers » de votre éditeur parlent tous regex nativement.

Pour la moitié JSON de la même boîte à outils, consultez notre aide-mémoire jq en ligne de commande.

1.1 Comment lire un motif regex (tutoriel expression régulière en 5 secondes)

La plupart des motifs se lisent plus facilement de gauche à droite, un token à la fois. Prenez ^[A-Z]\w+\d{2,4}$ comme exemple :

  • ^ ancre la correspondance au début de la chaîne. Rien ne peut venir avant.
  • [A-Z] matche exactement une lettre majuscule. Juste une — pas encore de quantificateur.
  • \w+ matche un ou plusieurs caractères de mot (lettres, chiffres, underscore).
  • \d{2,4} matche entre deux et quatre chiffres.
  • $ ancre à la fin de la chaîne. Rien ne peut venir après.

Le motif complet matche donc Order42, Job1999 et X07 (attendez, non — il faut au moins deux caractères de mot après X). Le savoir-faire consiste à lire d’abord les ancres, puis les classes de caractères, ensuite les quantificateurs, et enfin les frontières.

2. Tableau de référence rapide

C’est la section que la plupart des lecteurs viennent chercher. Copiez ce qu’il vous faut.

Métacaractères

MotifCorrespond à
.Tout caractère sauf le saut de ligne (ou n’importe quel caractère avec le flag s/dotall)
\dUn chiffre ([0-9], ou tous les chiffres Unicode avec le flag u)
\DUn non-chiffre
\wUn caractère de mot ([A-Za-z0-9_])
\WUn caractère non-mot
\sTout espace blanc (espace, tabulation, saut de ligne, …)
\STout caractère non blanc

Quantificateurs

MotifCorrespond à
*0 ou plus (gourmand)
+1 ou plus (gourmand)
?0 ou 1 (gourmand)
{n}Exactement n fois
{n,m}Entre n et m fois
{n,}n fois ou plus
*?, +?, ??, {n,m}?Versions paresseuses de chaque quantificateur

Ancres

MotifCorrespond à
^Début de chaîne (ou début de ligne avec le flag m)
$Fin de chaîne (ou fin de ligne avec le flag m)
\bFrontière de mot
\BNon-frontière de mot
\ADébut absolu de chaîne (Python)
\ZFin absolue de chaîne (Python)

Classes de caractères

MotifCorrespond à
[abc]L’un de a, b, c
[^abc]Tout sauf a, b, c
[a-z]Toute minuscule
[0-9]Tout chiffre
\p{L}Toute lettre Unicode (flag u en JS, par défaut dans le re de Python)

Groupes

MotifCorrespond à
(...)Groupe capturant
(?:...)Groupe non capturant
(?<name>...)Capture nommée (JS ES2018+) ; Python utilise (?P<name>...)
\1, \2Rétro-référence au groupe 1, 2

Lookaround

MotifCorrespond à
(?=...)Lookahead positif
(?!...)Lookahead négatif
(?<=...)Lookbehind positif
(?<!...)Lookbehind négatif

Flags

FlagEffet
iInsensible à la casse
mMultiligne : ^ et $ correspondent ligne par ligne
sDotall : . correspond aux sauts de ligne
gGlobal (JS) — trouve toutes les correspondances
uMode Unicode
ySticky (JS) — ancre sur lastIndex

3. Métacaractères et classes de caractères

3.1 Littéraux et caractères spéciaux

La plupart des caractères sont littéraux. Les 12 métacaractères qu’il faut échapper pour les utiliser tels quels sont :

. ^ $ * + ? ( ) [ ] { } | \

Oublier d’échapper . reste le bug regex le plus fréquent. \. correspond à un point littéral. Dans une classe de caractères, [.] correspond aussi à un point littéral : la plupart des métacaractères perdent leur pouvoir dans [...], sauf ], \, ^ (en première position) et - (au milieu).

3.2 Raccourcis de classes

Les classes raccourcies ont l’air simples jusqu’à ce qu’Unicode arrive :

// JavaScript — without the u flag, \d is ASCII only
/\d/.test('5');    // true
/\d/.test('٥');    // false (Arabic-Indic digit)
/\d/u.test('٥');   // false — even with u, \d stays ASCII in JS
/\p{N}/u.test('٥'); // true — \p{N} is the Unicode-aware digit class
# Python — re module treats \d as Unicode by default
import re
re.match(r'\d', '٥')  # <Match span=(0, 1)>
re.match(r'(?a)\d', '٥')  # None — (?a) forces ASCII

Si vous ne traitez que de l’ASCII anglais, \d et [0-9] font la même chose. Dès qu’un utilisateur colle un nom avec un accent, préférez \p{L} à \w.

3.3 Classes de caractères personnalisées

// JavaScript
/[A-Za-z][A-Za-z0-9_-]{2,29}/.test('valid_handle-1'); // true

// Negation and ranges combined
/[^aeiou\s]/g  // any non-vowel, non-whitespace character

Pour les catégories Unicode, \p{L} veut dire « toute lettre », \p{N} « tout chiffre », \p{Script=Han} « tout caractère Han ». JavaScript exige le flag u ; Python ne gère \p{...} qu’à travers le paquet PyPI regex, pas le module standard re.

Si vous travaillez en ligne de commande, vous croiserez aussi les classes de caractères POSIX :

Classe POSIXCorrespond àÉquivalent ASCII
[[:alpha:]]lettres[A-Za-z]
[[:digit:]]chiffres[0-9] (\d en JS/Python)
[[:alnum:]]lettres et chiffres[A-Za-z0-9]
[[:space:]]espaces blancs\s
[[:upper:]]majuscules[A-Z]
[[:lower:]]minuscules[a-z]

Les classes POSIX fonctionnent dans grep -E, sed -E et les autres outils qui suivent la regex étendue POSIX. Elles ne fonctionnent pas en JavaScript ni dans le re de Python — utilisez les équivalents abrégés (\d, \s, \w) à la place.

4. Quantificateurs : gourmand contre paresseux

4.1 Quantificateurs de base

/a*/.exec('aaab')      // ['aaa']     — 0 or more
/a+/.exec('aaab')      // ['aaa']     — 1 or more
/a?/.exec('aaab')      // ['a']       — 0 or 1
/a{2,3}/.exec('aaaab') // ['aaa']     — 2 to 3

4.2 Gourmand contre paresseux

Par défaut, les quantificateurs sont gourmands : ils prennent autant qu’ils peuvent, puis reculent pour faire tenir l’ensemble du motif. Ajoutez ? pour passer en mode paresseux.

const html = '<p>one</p><p>two</p>';

html.match(/<p>.*<\/p>/)[0];   // '<p>one</p><p>two</p>'   (greedy eats both)
html.match(/<p>.*?<\/p>/)[0];  // '<p>one</p>'             (lazy stops at first)

La version paresseuse est presque toujours celle que vous voulez pour extraire des balises ou des chaînes entre guillemets. Encore mieux : évitez . et utilisez une classe niée. <p>[^<]*</p> est plus rapide que <p>.*?</p> parce qu’il n’y a rien sur quoi revenir.

4.3 Backtracking catastrophique

C’est comme ça qu’une regex fait planter un serveur. Imbriquez un quantificateur dans un autre avec un chevauchement ambigu, et le moteur explore un nombre exponentiel de chemins avant d’abandonner.

// Don't do this
/(a+)+b/.test('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!'); // takes seconds

Pour 41 a suivis de !, le moteur essaie environ 2^41 points de découpe avant de conclure que le b manque. Trois façons d’éviter ça :

  1. Aplatir le motif : /a+b/ fait le même travail sans imbrication.
  2. Utiliser un groupe atomique (Python regex, PCRE, Java, Ruby) : (?>a+)+b. Une fois a+ matché, le moteur refuse de revenir en arrière dedans.
  3. Changer de moteur : regexp de Go, RE2 et la crate regex de Rust utilisent un NFA en temps linéaire et ne peuvent pas, par conception, faire de backtracking catastrophique.

JavaScript et le re de Python font tous deux du backtracking et n’ont pas de groupes atomiques dans la lib standard (le paquet PyPI regex de Python les ajoute). Si vous contrôlez la longueur de l’entrée, ça passe ; si elle vient d’un utilisateur, validez la longueur d’abord ou compilez à l’avance contre RE2.

5. Ancres et frontières de mots

5.1 ^ et $

Par défaut, ^ est le début de toute l’entrée et $ la fin. Avec le flag m (multiligne), ils deviennent le début et la fin de chaque ligne :

const log = 'INFO start\nERROR boom\nINFO done';
log.match(/^ERROR.*/);    // null    — single-line mode, ^ only matches index 0
log.match(/^ERROR.*/m);   // ['ERROR boom']

5.2 \b et \B

\b est une assertion de largeur nulle : il correspond à la position entre un caractère de mot (\w) et un caractère non-mot. Pratique pour rechercher un mot entier :

/\bcat\b/.test('the cat sat');     // true
/\bcat\b/.test('concatenate');     // false

Les frontières de mots sont définies sur \w, qui est ASCII par défaut. Les textes chinois, japonais et coréen n’ont pas d’espaces entre les mots, donc \b ne détecte pas les bords de mots ; il vous faut un tokenizer (jieba, MeCab) avant la regex, pas à sa place.

5.3 Mode multiligne

import re
text = "INFO ok\nERROR fail\nINFO done\n"

re.findall(r'^ERROR.*$', text)              # []
re.findall(r'^ERROR.*$', text, re.MULTILINE) # ['ERROR fail']

En JavaScript, la même chose s’écrit text.match(/^ERROR.*$/gm). Combinez m et g pour récupérer chaque ligne qui correspond.

6. Groupes, captures et rétro-références

6.1 Groupes capturants

Les parenthèses font deux choses : regrouper des sous-motifs pour les quantificateurs, et capturer la correspondance pour la réutiliser plus tard.

'2026-05-13'.match(/(\d{4})-(\d{2})-(\d{2})/);
// ['2026-05-13', '2026', '05', '13', index: 0, ...]

Les groupes sont numérotés de gauche à droite selon leur parenthèse ouvrante, à partir de 1.

6.2 Groupes non capturants

Quand vous voulez juste grouper, sans capturer, utilisez (?:...). C’est plus rapide et ça garde la numérotation propre :

/(?:https?):\/\/(\S+)/.exec('see https://go-tools.org');
// ['https://go-tools.org', 'go-tools.org']
// — the protocol is grouped but not captured; group 1 is the host

6.3 Groupes nommés

Nommer les groupes rend les motifs lisibles et survit aux refactorings.

// JavaScript (ES2018+)
const m = '2026-05-13'.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/);
m.groups.year;  // '2026'
# Python — note the (?P<...>) syntax
import re
m = re.match(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})', '2026-05-13')
m.group('year')  # '2026'

6.4 Rétro-références

Avec une rétro-référence, une partie ultérieure du motif répète ce qu’une capture antérieure a matché.

// Find any character that repeats consecutively
'bookkeeper'.match(/(\w)\1/g);   // ['oo', 'kk', 'ee']

// Match paired HTML tags by name
const tag = /<(\w+)>(.*?)<\/\1>/;
'<b>bold</b>'.match(tag);
// ['<b>bold</b>', 'b', 'bold']

En Python, \1 fonctionne à la fois dans le motif et dans le remplacement ; les références nommées s’écrivent (?P=name) dans le motif et \g<name> dans les remplacements re.sub.

7. Lookarounds : lookahead et lookbehind

Les lookarounds sont des assertions de largeur nulle. Ils vérifient une condition sans consommer de caractères, ce qui veut dire qu’on peut les enchaîner.

7.1 Lookahead

// Password: at least 8 chars, one digit, one uppercase, one lowercase
const strong = /^(?=.*\d)(?=.*[A-Z])(?=.*[a-z]).{8,}$/;
strong.test('Hunter2!');   // true
strong.test('hunter2!');   // false — no uppercase

// Negative lookahead — file names that are not .tmp
/^[\w-]+(?!\.tmp$)\.[a-z]+$/.test('report.csv'); // true

7.2 Lookbehind

Le lookbehind est l’image miroir : il vérifie ce qui vient avant la position courante.

// Extract a price after a currency symbol — keep the number, drop the $
'price: $42.50'.match(/(?<=\$)\d+(\.\d+)?/);   // ['42.50', '.50']

// Negative lookbehind — match Bond but not James Bond
'Mr. Bond'.match(/(?<!James )Bond/);  // ['Bond']
'James Bond'.match(/(?<!James )Bond/); // null

7.3 Lookbehind en JavaScript et en Python

C’est un des rares endroits où les deux moteurs divergent assez pour casser un motif quand vous le portez.

MoteurLargeur du lookbehind
JavaScript (V8, SpiderMonkey, JSC 16.4+)Largeur variable depuis ES2018. (?<=\d+) est valide.
re standard de PythonLargeur fixe uniquement. (?<=\d+) lève error: look-behind requires fixed-width pattern.
Paquet PyPI regex de PythonLargeur variable supportée. import regex; regex.search(r'(?<=\d+)abc', '12abc').

Contournement en Python : réécrivez le lookbehind avec une répétition fixe ((?<=\d{3})), ou capturez le préfixe et coupez-le après la correspondance.

8. Flags et modificateurs

8.1 i — insensible à la casse

/error/i.test('FATAL ERROR'); // true
re.search(r'error', 'FATAL ERROR', re.IGNORECASE)  # <Match span=(6, 11)>

8.2 m et s

m transforme ^ et $ en ancres par ligne. s (dotall) laisse . matcher les sauts de ligne. Ils sont indépendants ; combinez-les quand vous voulez les deux.

/<script>(.*?)<\/script>/s.exec('<script>\nalert(1)\n</script>')[1];
// '\nalert(1)\n'  — without s, the . would refuse the newlines

8.3 g — global

En JavaScript, g change l’API plutôt que la correspondance elle-même. Sans g, String.match renvoie les groupes capturés ; avec g, il renvoie chaque chaîne matchée. Utilisez matchAll pour garder les groupes capturés sur toutes les correspondances.

const text = 'a=1 b=2 c=3';

text.match(/(\w)=(\d)/);     // first match with groups
text.match(/(\w)=(\d)/g);    // ['a=1', 'b=2', 'c=3'] — no groups
[...text.matchAll(/(\w)=(\d)/g)]; // every match, with groups

Python n’utilise pas gre.findall, re.finditer et re.sub sont les variantes globales.

8.4 u — Unicode et \p{...}

// Match any Han character (Chinese, Japanese kanji)
/\p{Script=Han}+/gu.test('Hello 世界'); // true

// Match emoji (extended pictographic)
/\p{Extended_Pictographic}/u.test('👋'); // true

En Python, Unicode est activé par défaut ; re.findall(r'[一-鿿]+', text) est l’équivalent pour la plage Han. Pour les échappements complets de propriétés Unicode, passez par le paquet PyPI regex : regex.findall(r'\p{Script=Han}+', text).

9. Motifs courants à utiliser au quotidien

9.1 Validation d’email

Soyez honnête sur la version qu’il vous faut.

// The 95% pattern — what most form validators use
const email = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
email.test('a@b.co');  // true

// The "I really want to be RFC 5322-ish" pattern
const rfc = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;

La vérité : une validation d’email RFC 5322 complète en regex pure fait environ 6000 caractères et reste fausse sur des cas limites. Utilisez le motif à 95 %, puis envoyez un email de vérification. C’est le seul test qui marche vraiment.

9.2 Extraction d’URL

const urlPattern = /https?:\/\/[^\s<>"]+/g;
const found = 'See https://example.com/a?b=1 and http://x.io'.match(urlPattern);
// ['https://example.com/a?b=1', 'http://x.io']

Une fois l’URL extraite, vous voudrez en général inspecter sa chaîne de requête. Collez-la dans notre décodeur et encodeur URL et vous lirez les paramètres percent-encodés d’un coup d’œil. Pour le panorama complet du quand encoder ou décoder, lisez le guide d’encodage et décodage d’URL.

9.3 Numéros de téléphone

// E.164 — international, optional + and 1-3 digit country code
const e164 = /^\+?[1-9]\d{1,14}$/;
e164.test('+14155551234');  // true

// North American Number Plan with separators
const nanp = /^(\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$/;
nanp.test('(415) 555-1234'); // true

Pour aller plus loin que « est-ce que cette forme est plausible », utilisez libphonenumber. Une regex ne peut pas vérifier qu’un indicatif régional existe vraiment.

9.4 IPv4 et IPv6

// IPv4 — strict 0-255 per octet
const ipv4 = /^((25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(25[0-5]|2[0-4]\d|1?\d?\d)$/;
ipv4.test('192.168.1.1');   // true
ipv4.test('999.0.0.1');     // false

// IPv6 — the simplified form. The full RFC 4291 pattern is ~600 chars.
const ipv6simple = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/;
ipv6simple.test('2001:0db8:85a3:0000:0000:8a2e:0370:7334'); // true

Pour le vrai IPv6 avec la notation abrégée ::, l’IPv4 intégré et les identifiants de zone, utilisez isIP() de node:net ou ipaddress.ip_address() de Python. Le faire en regex pure est un rite de passage, puis une dette de maintenance.

9.5 Dates et timestamps ISO 8601

// Date only — YYYY-MM-DD
const isoDate = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
isoDate.test('2026-05-13'); // true

// Date + time + optional fractional seconds + Z or offset
const iso = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/;
iso.test('2026-05-13T09:30:00.123Z'); // true

ISO 8601 a l’air simple et cache plein de pièges : secondes intercalaires, dates semaines (2026-W19), dates ordinales (2026-133). Pour les secondes contre millisecondes d’epoch et les décalages de fuseau, consultez le guide des timestamps Unix.

10. Workflows de recherche-remplacement avec regex

10.1 JavaScript — String.replace avec $1

// Reformat US dates: MM/DD/YYYY -> YYYY-MM-DD
'05/13/2026'.replace(/(\d{2})\/(\d{2})\/(\d{4})/, '$3-$1-$2');
// '2026-05-13'

// Use a callback when the replacement is conditional
'price 42 dollars'.replace(/(\d+) dollars/, (_, n) => `$${n}`);
// 'price $42'

$1, $2, etc. référencent les groupes numérotés. $<name> référence les groupes nommés. $& est la correspondance complète ; $$ est un $ littéral.

10.2 Python — re.sub avec \1 et callbacks

import re

# Same date reformat as above
re.sub(r'(\d{2})/(\d{2})/(\d{4})', r'\3-\1-\2', '05/13/2026')
# '2026-05-13'

# Callback — uppercase every email address in a string
def upper_email(m):
    return m.group(0).upper()

re.sub(r'[\w.-]+@[\w.-]+', upper_email, 'mail me at hi@go-tools.org')
# 'mail me at HI@GO-TOOLS.ORG'

Dans les remplacements, Python utilise \1 ou \g<name>. Le préfixe raw string r'...' n’est pas optionnel : sans lui, \1 devient un caractère littéral.

10.3 CLI : sed, grep, ripgrep, jq

Pour les refactorings en masse en ligne de commande, la regex passe du script au shell :

# ripgrep — find every TODO with a name attached
rg -n '\bTODO\(([^)]+)\)' --replace 'TODO(\1)'

# grep -E with anchors — failed login lines from auth.log
grep -E '^[A-Z][a-z]{2} +[0-9]+ .*Failed password' /var/log/auth.log

# sed — strip trailing whitespace, in-place, across a tree
find . -name '*.md' -print0 | xargs -0 sed -i -E 's/[[:space:]]+$//'

ripgrep utilise la crate regex de Rust (style RE2, temps linéaire, pas de lookbehind). grep -E et sed -E utilisent la regex étendue POSIX, qui n’a pas \d : préférez [0-9] ou [[:digit:]]. Quand les données sont du JSON, troquez la regex pour jq ; consultez l’aide-mémoire jq pour une fiche de référence parallèle.

11. Pièges courants

11.1 Oublier d’échapper .

Un vrai bug qu’on a déjà livré : un masqueur de logs censé cacher les adresses IP.

// Wrong — matches '192a168b1c1' too
/(\d+).(\d+).(\d+).(\d+)/.test('192a168b1c1');  // true

// Right
/(\d+)\.(\d+)\.(\d+)\.(\d+)/.test('192a168b1c1'); // false

Dans une classe de caractères, . est déjà littéral, donc [.] et \. fonctionnent tous les deux. Partout ailleurs, échappez-le.

11.2 Le .* gourmand mange trop

'<a href="x"><b>bold</b></a>'.match(/<(.*)>/)[1];
// 'a href="x"><b>bold</b></a'  — the whole thing!

Le .* gourmand parcourt jusqu’à la fin de la chaîne, puis revient en arrière jusqu’à trouver >, c’est-à-dire le dernier > de l’entrée. Soit vous passez en paresseux (.*?), soit, plus rapide et plus clair, vous utilisez une classe niée ([^>]*).

11.3 Ancres multilignes

Une confusion classique : ^ et $ ne matchent pas les sauts de ligne par défaut. Ils matchent les positions au début et à la fin de toute l’entrée. C’est le flag m qui les transforme en ancres par ligne. C’est le flag s qui laisse . traverser les sauts de ligne. Ils sont orthogonaux et vous voudrez en général les deux pour analyser des logs.

11.4 ReDoS et comment le désamorcer

ReDoS (regex denial of service) est la version production du backtracking catastrophique. Les défenses :

  1. Analyse statique : des outils comme safe-regex, recheck et la règle no-misleading-character-class d’ESLint repèrent les motifs dangereux avant la mise en prod.
  2. Groupes atomiques (Python regex, PCRE, Ruby, Java) : (?>...) empêche le moteur de revenir dans le groupe pendant le backtrack.
  3. Quantificateurs possessifs (*+, ++, ?+ en PCRE/Java) : même idée, syntaxe plus concise.
  4. Passer à un moteur sans backtracking : regexp de Go, RE2, la crate regex de Rust et le binding Python re2 tournent tous en temps linéaire. ripgrep est le déploiement de RE2 le plus répandu en pratique.
  5. Valider la longueur de l’entrée en amont. Une bombe regex de 10 Ko est un bug ; un plafond de 10 octets sur l’entrée tient en une ligne de code.

Pour un inventaire plus large des outils qui s’utilisent avec regex (formateurs, décodeurs, convertisseurs), consultez notre guide des outils de développement.

Avant de livrer un motif complexe, testez-le en interactif. regex101.com bascule entre les saveurs PCRE, JavaScript, Python et Go, explique chaque token en clair et montre le backtracking pas à pas pour que vous repériez les motifs catastrophiques avant que la production ne s’en charge.

12. FAQ

Quelle est la différence entre * et + en regex ?

* correspond à zéro ou plusieurs occurrences (donc une chaîne vide passe) ; + correspond à une ou plusieurs (il en faut au moins une). a* matche '', 'a', 'aaaa'. a+ matche 'a' et 'aaaa' mais pas ''.

Comment faire correspondre une regex sur plusieurs lignes ?

Activez le flag multiligne (/.../m en JavaScript, re.MULTILINE en Python) pour que ^ et $ s’ancrent à chaque ligne. Pour que . traverse aussi les sauts de ligne, ajoutez le flag dotall (s en JavaScript, re.DOTALL en Python).

La regex est-elle identique en JavaScript et en Python ?

La syntaxe de base (quantificateurs, ancres, classes de caractères, groupes basiques) est identique à 90 %. Les deux vraies différences : JavaScript (ES2018+) supporte le lookbehind de largeur variable et écrit les groupes nommés (?<name>...) ; le re standard de Python exige un lookbehind à largeur fixe et utilise (?P<name>...). Pour du lookbehind à largeur variable en Python, installez le paquet regex depuis PyPI.

Pourquoi ma regex fait-elle du backtracking catastrophique ?

Vous avez des quantificateurs imbriqués avec des correspondances qui se chevauchent, comme (a+)+ ou (a|a)*. Sur une entrée qui matche presque mais échoue près de la fin, le moteur essaie chaque découpe du quantificateur interne, soit un nombre exponentiel de chemins. Corrigez avec un groupe atomique (?>a+)+, un quantificateur possessif a++, ou en passant à un moteur sans backtracking comme RE2 ou regexp de Go.

Puis-je utiliser le lookbehind en JavaScript ?

Oui. Les lookbehind positif (?<=...) et négatif (?<!...) sont dans V8 (Chrome, Node.js), SpiderMonkey (Firefox) et JavaScriptCore (Safari 16.4+) depuis ES2018. Le lookbehind à largeur variable est supporté. Pour les Safari plus anciens, transpilez avec Babel ou détectez la fonctionnalité avec un try/catch autour de new RegExp.

Comment matcher un point . littéral en regex ?

Échappez-le avec un antislash : \. correspond à un point littéral. Dans une classe de caractères, le point est déjà littéral, donc [.] comme [\.] fonctionnent. Hors classe, un . non échappé est un métacaractère qui veut dire « tout caractère sauf le saut de ligne » (ou n’importe quel caractère avec le flag dotall).

Que signifie \s en regex ?

\s matche tout caractère d’espace blanc — espace, tabulation (\t), saut de ligne (\n), retour chariot (\r), tabulation verticale et saut de page. En mode Unicode (JavaScript avec le flag u, Python par défaut en 3.x), il matche aussi le NBSP ( ) et les autres points de code d’espace Unicode. L’inverse \S matche tout caractère qui n’est pas un espace blanc.

Les expressions régulières sont-elles sensibles à la casse ?

Par défaut, oui. /cat/ ne matche pas Cat. Activez le flag d’insensibilité à la casse pour ignorer la casse : JavaScript ajoute i (/cat/i), Python utilise re.IGNORECASE ou le groupe inline (?i). En mode Unicode, le repliage de casse couvre aussi les paires délicates comme ß↔SS et le I turc avec ou sans point.

Articles connexes

Voir tous les articles