Les regex, aussi effrayantes que puissantes


Une fois n’est pas coutume, l’introduction de cet article va comporter une image, qui, à elle seule représente le désamour des développeurs pour les expressions régulières !

""

Malheureusement, le dev de droite est resté coincé avec sa regex pendant deux ans, et maintenant, il élève des chèvres au fin fond du Cantal ! Pour éviter la surpopulation d’éleveurs de chèvres dans le Cantal, il faut dédiaboliser les regex et apprendre à mieux les connaître.

L’origine des regex

Les regex trouvent leur origine aux États-Unis, dans des équipes de recherche travaillant sur le système nerveux aux alentours des années 40. De ces travaux, Stephen Cole Kleene, logicien de son état, a décrit les modèles neuronaux découverts en termes d’ensembles réguliers. On le considère comme l’inventeur des expressions régulières.

Mmmmmmmmh, ce n’est pas clair du tout ton affaire ! Dissipons le brouillard qui entoure les regex, au travers d’un cas d’utilisation simple.

Regex : niveau 1

Quand vous recherchez un mot, le nom d’une variable, une phrase dans une page web, en général, vous faites un Ctrl+F afin d’ouvrir un champ de recherche qui recherche pour vous les occurrences d’un mot dans cette page.

Ce champ de recherche utilise une regex afin de retrouver le mot saisi !

L’expression régulière la plus simple est donc un mot, une phrase, une suite de caractères quelconques (lettres, chiffres, symboles).

expression régulière : régulier
mots détectés : régulier

Vraiment ? C’est ça une expression régulière ???

OUI et NON, ce n’est pas “que” ça, c’est une expression régulière presque réduite à sa plus simple expression. Si on va au bout de l’idée, un simple caractère est une expression régulière. La force des regex, c’est qu’elles permettent de rechercher des ensembles de chaînes de caractères plus ou moins spécifiques.

Du coup, comment on peut rechercher à la fois les termes “régulier” et “régulières” ? Il faut plonger dans la mécanique des regex (j’aurais bien parlé de mécanique des expressions régulières, mais ça faisait trop de “réguliers” dans cette phrase 😅).

Regex : niveau 2

Dans un premier temps, on peut rechercher le premier mot, puis le second. Pas très pratique dès que le document est un peu long, et ça fait repasser 2 fois au même endroit (pour peu qu’on ait seulement deux termes proches).

Ce qui fait la force des regex, c’est qu’elles analysent les textes par groupes de chaînes de caractères, d’un simple caractère à autant de caractères qu’on le souhaite.

Dans notre cas, on se rend compte que les deux mots ont des caractéristiques communes.

régulier
régulière
(réguli) e ou è (r) e ou rien

On commence à voir se dessiner des groupes de caractères communs, séparés par d’autres groupes de caractères qui varient dans une gamme de possibilités limitées.

réguli n’est plus un mot, il s’agit maintenant d’un groupe de caractères.

Fragmenter pour mieux rechercher

Pour rechercher les mots “régulier” et “régulière” dans un texte, on pourra utiliser l’expression régulière suivante :

réguli

Vous trouverez effectivement les occurrences de “régulier” et de régulière”, mais vous trouverez également les occurrences de “régulièrement”, “réguliez”, et je vous passe les trois possibilités restantes.

Il y a peu de chances que “régulièrement” et “réguliez” soient fondamentaux dans votre recherche, vous voulez donc être plus spécifique.

Pour cela, nous allons aborder les méta-caractères.

Les méta-caractères

Vous ne le savez pas encore, mais vous avez déjà vu deux de ces méta-caractères dans cet article. Une idée ?

Les parenthèses qui délimitent un groupe de caractères sont des méta-caractères, c’est-à-dire des caractères qui donnent un sens, une fonction aux différents éléments d’une regex.

Parfois, on est amené à rechercher deux mots se trouvant dans une même phrase, mais n’étant jamais consécutifs pour des raisons grammaticales. Rechercher un élément, puis l’autre ne vous aidera pas à trouver ce que vous cherchez. Dans ce type de cas, vous pouvez utiliser la combinaison de 2 méta-caractères :

regex : Dans une regex(.*)contient
résultat : Dans une regex, un groupe de caractères est représenté par les caractères qu'il contient

Combinés, .* permettent donc de rechercher une chaîne de caractères de longueur quelconque, contenant des lettres, des chiffres, des signes de ponctuation, etc.

Ces méta-caractères ne sont pas les seuls, on trouve également les suivants :

méta-caractère explication regex exemple
+ 1 à n fois le caractère qui le précède dev+ dev, devv, devvv
? 0 ou n occurrences du caractère ou du groupe précédent dev? de, dev, devv
^ le début d'une ligne ^dev dev, deva, devb
$ la fin d'une ligne devenir$ dev en devenir
\b indique la limite d'un mot \bdev\b dev mais pas devenir
| l'élément de gauche ou celui de droite devenir\|deviner devenir, deviner
{m} le nombre d'occurrences de l'élément précédent 1{3} 111
{m, n} l'intervalle d'occurrences recherché 1{3, 4} 111, 1111
{n,} nombre minimum d'occurrences à n 1{2,} 11, 111, 1111
[abc] l'un des caractères [dev] d, e, v
[^abc] aucun de ces caractères [^dev] ni d, ni e, ni v
\ caractère d'échappement d'un méta-caractère \^ ^

D’autres éléments permettent de caractériser des ensembles de caractères plus larges, les groupes.

Les groupes de caractères

Il existe également des groupes de caractères particuliers, permettant de traiter des groupes plus larges :

groupe caractères représentés
[A-Za-z] à toutes les lettres de l'alphabet, majuscules et minuscules non accentuées
[0-9] les chiffres
\d les chiffres
\w caractères alphanumériques et l'underscore
\s un ou plusieurs espaces, tabulations ou retours à la ligne
\n saut de ligne

Même s’il ne paraît pas évident de prime abord que tous ces éléments sont ici pour nous faciliter la vie, on peut essayer de les combiner afin d’avoir un aperçu de leur utilité.

Boss final : construire une regex

Les cas sont infinis. Ma première confrontation avec les regex remonte à la conversion de spécifications fonctionnelles de plusieurs dizaines de pages au format docx vers un format md.

Quand vous avez des règles métier par dizaines, qui possèdent une définition et de multiples ancres, les regex peut vous sauver la vie ! J’y ai été confronté, avec des documents de plusieurs dizaines de pages, truffés de références à des ancres ramenant à la définition de règles métier.

Sur un site web, les formulaires sont de parfaits exemples. Ils doivent s’assurer que votre adresse mail a le bon format, ainsi que votre numéro de téléphone. Avant de devenir développeur, jamais je ne m’étais posé la question de la technique employée pour détecter qu’il y a une erreur dans un formulaire, ou au contraire déterminer que la valeur renseignée est valide.

Vous vous en doutez, la réponse est : les regex !

Une adresse mail a le format suivant : devendevenir@devendevenir.fr. Si on compare plusieurs adresses mail, on constate qu’elles ont toutes la même structure :

utilisateur@domaine.extension

La norme RFC 3696 précise les caractères spéciaux que peut contenir une adresse email. La norme est très permissive, et de nombreux services appliquent des restrictions supplémentaires. Nous allons donc considérer que l’utilisateur d’un email peut contenir un point, un underscore ou un tiret.

Décomposons chaque partie de notre adresse email sous la forme d’une expression régulière :

début de la chaîne de caractères : ^
utilisateur : [a-zA-Z0-9._-]
séparation entre utilisateur et domaine : @
domaine : [a-zA-Z0-9.-]
séparation entre domaine et extension : \.
extension : [a-zA-Z]{2,}
fin de la chaîne de caractères : $

Pris séparément, les fragments de la regex sont compréhensibles, tandis que la forme complète est beaucoup plus impressionnante et semble donc plus complexe.

^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$

Si on reprend chaque partie de la regex, on peut voir les contraintes suivantes :

  • l’utilisateur peut avoir n’importe quel caractère alphanumérique en plus du point, de l’underscore et du tiret comme dit plus haut.
  • l’arobase est obligatoire.
  • le domaine peut avoir n’importe quel caractère alphanumérique en plus du point et du tiret.
  • l’extension comporte uniquement des lettres, et au minimum 2. C’est cette partie qui marque la fin de la chaîne de caractères.

Regex : cheat sheet

Je ne suis pas toujours convaincu de l’utilité de l’IA dans certains domaines, mais force est de constater que la génération d’expressions régulières (dans des cas simples) est assez efficace.

Par ailleurs, il existe de nombreux sites web permettant de construire, tester et débugger des expressions régulières. On citera notamment regex101, qui est un redoutable outil pour qui est à l’aise avec l’anglais. Ce site vous permettra de saisir une regex et de la tester. Vous pouvez également coller une expression régulière pour laquelle le site génèrera automatiquement une explication détaillant le rôle de chaque partie de votre regex.

Conclusion

On ne croise pas tous les jours des expressions régulières, mais depuis que je suis développeur, je les ai déjà côtoyées 2 fois en 4 ans, dans des contextes très différents (conversion de spécifications fonctionnelles de docx vers md, analyse d’une image en base 64 défaillante dans un formulaire).

La première rencontre a été la plus difficile à cause du volume de texte concerné, tandis que la deuxième a été beaucoup plus facile, car le travail effectué la première fois m’a directement servi et facilité la tâche.

Et la prochaine fois que vous recherchez 1 ou 2 termes dans un projet (variable, fonction, extrait de code), pensez-y. La plupart des IDE permettent l’utilisation de regex dans leurs outils de recherche et de remplacement.

Cet article vous a plu ? Contactez-moi sur sur  LinkedIn  😉 !

Articles en lien