Imagine que tu cherches dans un document tous les numéros de téléphone français.
Ils peuvent être écrits de dizaines de façons, 06 12 34 56 78, 0612345678,
+33 6 12 34 56 78, 06.12.34.56.78, et la liste continue.
Tu pourrais écrire une règle de recherche pour chaque format, et en oublier la moitié.
Ou tu pourrais utiliser une expression régulière, souvent abrégée en "regex",
un mini-langage qui décrit des motifs de texte.
Ce chapitre ne va pas faire de toi un expert. Il va te permettre de lire une regex
qu'un développeur te met sous les yeux, de comprendre pourquoi la validation
d'un formulaire refuse une adresse pourtant valide, et de savoir quand ce n'est
pas le bon outil.
Le principe
Une expression régulière est une formule qui décrit un schéma de texte.
Au lieu de chercher un texte exact, on décrit ce à quoi le texte ressemble.
L'idée n'est pas récente. Elle vient des mathématiques des années 1950 et a été
intégrée aux outils Unix dans les années 1970, avec des commandes comme
grep. Depuis, à peu près tous les langages de programmation
et tous les éditeurs de texte savent en manipuler.
Commençons par les briques de base.
- [0-9] : un chiffre quelconque, de 0 à 9. Les crochets décrivent un ensemble de caractères possibles, et on n'en prend qu'un seul.
- [a-z] : une lettre minuscule quelconque. On peut cumuler,
[a-zA-Z0-9]accepte n'importe quelle lettre ou n'importe quel chiffre. - [^0-9] : le chapeau en début de crochets inverse la règle, ici n'importe quel caractère sauf un chiffre.
- . (un point) : n'importe quel caractère. Si tu veux un vrai point,
il faut l'échapper en écrivant
\.. - | : une alternative, qui porte cette fois sur tout ce qui l'entoure.
chat|chientrouve l'un ou l'autre.
À part l'alternative, ces briques décrivent un seul caractère à la fois. Pour en décrire plusieurs, on ajoute un quantificateur juste derrière.
- + : un ou plusieurs de ce qui précède.
- * : zéro ou plusieurs de ce qui précède.
- ? : zéro ou un, autrement dit optionnel.
- {3} : exactement trois fois.
- {2,} : au moins deux fois.
- {2,4} : entre deux et quatre fois.
Quelques raccourcis très courants reviennent tout le temps et méritent d'être reconnus au premier coup d'oeil.
- \d : un chiffre, l'équivalent exact de
[0-9]. - \w : un caractère de mot, c'est-à-dire une lettre non accentuée, un chiffre ou un tiret bas.
- \s : un espace, une tabulation ou un retour à la ligne.
- \b : une frontière de mot. C'est ce qui distingue une recherche
de "chat" qui trouve aussi "chaton" et "achat" d'une recherche
\bchat\bqui ne trouve que le mot entier.
Enfin, deux symboles servent à ancrer le motif. Le chapeau ^ en début
d'expression signifie "à partir du début du texte", et le dollar $
à la fin signifie "jusqu'à la fin du texte". Sans eux, on cherche le motif
n'importe où dans le texte.
C'est une source de bugs classique en validation de formulaire. Si tu vérifies
un code postal avec \d{5} sans ancres, la saisie
"mon code est 75001 merci" passe le test, puisque cinq chiffres consécutifs
y figurent bien. Avec ^\d{5}$, seule une saisie composée
exactement de cinq chiffres est acceptée.
Un exemple déroulé
Reprenons la promesse du début. Voici une expression qui reconnaît les principaux formats de numéro français.
(?:0|\+33 ?)[1-9](?:[ .-]?[0-9]{2}){4}Elle a l'air effrayante. Elle ne l'est pas, il suffit de la lire de gauche à droite, morceau par morceau.
- (?:0|\+33 ?) : soit un zéro, soit l'indicatif +33 éventuellement suivi
d'un espace. Les parenthèses servent à regrouper une portion de motif,
et le
?:juste après la parenthèse ouvrante indique simplement qu'on ne veut pas mémoriser ce morceau pour le réutiliser ensuite. - [1-9] : le premier chiffre du numéro, qui n'est jamais un zéro.
- [ .-]? : un séparateur optionnel, au choix un espace, un point ou un tiret. Dans des crochets, le point ne veut plus dire "n'importe quel caractère", il redevient un simple point.
- [0-9]{2} : deux chiffres.
- {4} à la fin : le groupe séparateur plus deux chiffres se répète quatre fois, ce qui donne bien huit chiffres après le premier.
Déroulons sur "06 12 34 56 78". Le zéro initial est reconnu, puis le 6,
puis quatre fois de suite un espace suivi de deux chiffres. Sur "0612345678",
même chose sans les espaces, puisque le séparateur est optionnel.
Sur "+33 6 12 34 56 78", c'est la branche \+33 ? qui s'applique
et la suite est identique.
Note qu'elle accepte aussi des écritures incohérentes comme "06 12.34-56 78", qui mélange les séparateurs. C'est très souvent le cas, une regex reconnaît un peu plus large que ce qu'on avait en tête. Tant qu'elle ne rejette pas de numéros valides, c'est généralement un compromis acceptable.
Où les utilise-t-on ?
Les regex sont partout en informatique, et pas uniquement dans le code.
- La validation de formulaires : vérifier qu'un email, un numéro de téléphone ou un code postal est au bon format avant de l'envoyer au serveur.
- La recherche et le remplacement : trouver et modifier des motifs dans du texte. "Remplace tous les numéros de carte bancaire par des étoiles." La fonction Rechercher/Remplacer de ton éditeur de texte propose presque toujours une option regex.
- L'extraction de données : récupérer des informations structurées
depuis du texte brut, par exemple toutes les dates d'un document
avec
\d{2}/\d{2}/\d{4}. - Le filtrage de logs : retrouver les lignes contenant une erreur précise parmi des millions d'autres. Les outils de monitoring reposent massivement sur ce mécanisme.
- La configuration serveur : les règles de redirection d'URL d'un site web s'écrivent avec des regex. Quand tu demandes "toutes les vieilles URLs /produit/... doivent rediriger vers /p/...", c'est une regex qui fait le travail. Et c'est là que le remplacement entre en jeu, on capture un morceau du motif entre parenthèses, l'identifiant du produit, pour le réinjecter dans la nouvelle adresse. Reconnaître n'est que la moitié de l'usage, réécrire est l'autre.
- Tes propres outils : tu en écris probablement déjà sans le savoir. Les filtres avancés d'un outil de mesure d'audience, les segments d'un CRM, les règles de tri d'une messagerie et la recherche de certains tableurs acceptent des regex. "Toutes les pages dont l'adresse commence par /blog/ et se termine par un chiffre", c'est une regex de dix caractères.
Un détail explique à lui seul la moitié des "ma recherche ne trouve rien". Par défaut, majuscules et
minuscules ne sont pas équivalentes, chat ne trouve pas "Chat". La plupart des outils proposent
une option pour ignorer la casse, souvent une petite case ou un drapeau i à côté du champ.
Exemple :
L'équipe support exporte 40 000 messages clients et veut isoler ceux
qui contiennent un numéro de commande. Les numéros ont tous la forme
CMD suivie de six chiffres. Une recherche avec CMD\d{6}
règle la question en quelques secondes, là où un filtre par mot-clé
aurait ramené tous les messages contenant le mot "commande".
La réputation
Les expressions régulières ont une réputation bien méritée. Elles sont
incroyablement puissantes et souvent illisibles, y compris pour la personne
qui les a écrites trois mois plus tôt.
Une blague popularisée par le développeur Jamie Zawinski en 1997 résume la chose.
Certains, face à un problème, se disent "je sais, je vais utiliser une expression régulière". Ils ont maintenant deux problèmes.
L'exemple canonique est la validation d'adresse email. Le motif suivant est celui qu'on utilise en pratique.
^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
Il se lit ainsi. Un ou plusieurs caractères parmi les lettres, les chiffres,
le point, le tiret bas, le pourcentage, le plus et le tiret. Puis une arobase.
Puis un ou plusieurs caractères parmi les lettres, les chiffres, le point
et le tiret. Puis un point, cette fois échappé donc bien littéral.
Puis au moins deux lettres pour l'extension.
Il accepte marie.dupont@gmail.com, contact@entreprise.fr ou encore
jean+newsletter@mail.co.uk. Il refuse en revanche les adresses avec accents
et quelques constructions exotiques pourtant légales.
Une regex qui validerait les adresses selon la spécification officielle complète
fait plus de 6 000 caractères. Personne ne la relit, personne ne la maintient,
et elle laisse quand même passer des adresses qui n'existent pas.
C'est pourquoi la bonne pratique consiste à faire une validation grossière
du format, puis à envoyer un email de confirmation. Seule la réception
prouve que l'adresse est réelle.
Les pièges à connaître
Trois travers reviennent assez souvent pour valoir la peine d'être signalés.
La gourmandise
Par défaut, les quantificateurs sont "gourmands", ils prennent le plus de texte possible.
Appliqué au texte <b>gras</b>, le motif <.+>
ne capture pas la balise ouvrante seule mais la chaîne entière, du premier chevron
au dernier. Ajouter un point d'interrogation après le quantificateur,
<.+?>, le rend "paresseux" et lui fait s'arrêter au plus tôt,
sur <b>.
C'est l'explication de la moitié des "ma regex prend trop de texte".
Les accents et l'alphabet
[a-z] et \w ne couvrent que l'alphabet latin non accentué.
Un motif écrit avec ces raccourcis rejettera "Benoît", "Nuñez" ou n'importe quel nom
en cyrillique ou en arabe. Sur un service qui vise l'international, c'est une source
de discrimination silencieuse, et sur un formulaire d'inscription, ce sont des clients refusés à la porte
pour le seul crime de porter leur nom. Les regex modernes savent raisonner sur les catégories
Unicode, encore faut-il y penser.
La performance
Certaines expressions mal construites, typiquement celles qui empilent
des quantificateurs imbriqués comme (a+)+$, peuvent exploser
en temps de calcul sur une chaîne un peu longue. Le moteur essaie
un nombre astronomique de combinaisons avant d'abandonner.
C'est une véritable famille de failles de sécurité, connue sous le nom de ReDoS.
Un attaquant envoie une chaîne soigneusement choisie dans un champ de formulaire
et bloque le serveur avec une seule requête. Des pannes majeures de services web
ont été causées par une regex de ce type déployée en production.
En pratique
Personne n'apprend les regex par coeur. Les développeurs en utilisent
une poignée de simples au quotidien et passent par des outils en ligne
comme regex101.com pour construire et tester les plus complexes.
Ces outils décomposent l'expression morceau par morceau et montrent en direct
ce qui est reconnu, ce qui rend l'apprentissage bien plus facile qu'à la lecture.
Aujourd'hui, la façon la plus courante d'obtenir une regex complexe est de la demander à un assistant
d'IA, et ça marche très bien. À une condition,
la tester sur de vrais exemples, y compris et surtout sur ceux qui doivent être refusés. Une regex qui accepte
tout ce qu'on lui montre a l'air de fonctionner, c'est celle qui laisse passer "mon code est 75001 merci".
De mon expérience, le vrai réflexe à acquérir est ailleurs. Avant d'écrire
une regex, demande-toi si un outil dédié n'existe pas. Pour découper du texte
sur une virgule, une simple fonction de découpage suffit. Pour analyser du
JSON ou du HTML, il faut un analyseur syntaxique,
jamais une regex. Vouloir extraire des données d'une page HTML avec une expression
régulière est un classique du genre, et ça finit toujours mal.
Et si tu es côté produit, retiens surtout ceci. Quand un utilisateur se plaint
que le formulaire refuse son adresse ou son numéro pourtant valides,
la cause est presque toujours une regex de validation trop stricte.
Une règle trop serrée fait perdre des inscriptions, ce qui coûte bien plus cher
que quelques saisies un peu farfelues à nettoyer plus tard.