Expressions régulières

Arthur Perret (Université Bordeaux Montaigne)

🚧 Page en construction — Dernière mise à jour : 2022-03-29

Les expressions régulières permettent de chercher des motifs dans des données textuelles. Elles améliorent notamment la fonctionnalité rechercher-remplacer des éditeurs de texte, traitements de texte et autres tableurs. Elles sont extrêmement utiles pour le traitement de données mais aussi pour la pratique du format texte.

Trouver une information précise dans un fichier informatique, comme le mot « bonjour », c’est plutôt simple : on ouvre ce fichier dans un programme doté d’une fonctionnalité de recherche, on saisit le mot « bonjour », et on obtient les occurrences de cette expression dans le fichier, s’il y en a.

Mais comment trouver le mot « bonjour » uniquement à un endroit précis, par exemple au début du texte ? Ce n’est pas possible avec une recherche classique.

Autre exemple : comment trouver toutes les occurrences particulières d’un motif, par exemple tous les numéros de téléphone dans un tableur, ou tous les URL dans un fichier HTML ? Encore une fois, la recherche classique ne le permet pas.

Une solution possible, c’est d’utiliser des expressions régulières.

Définition

Une expression régulière est une chaîne de caractères type, un motif (pattern), qui décrit un ensemble de chaînes de caractères possibles. C’est un modèle interprété par un moteur, lequel va essayer de trouver des correspondances du modèle dans le texte recherché.

Les expressions régulières relèvent du formalisme. Elles ne comprennent pas le langage naturel, et elles ne fonctionnent pas sur la base de mots ou de phrases. À la place, elles sont constituées de caractères, dont certains jouent un rôle spécial. L’ensemble des caractères spéciaux et leurs règles d’écriture constituent la syntaxe des expressions régulières. Cette syntaxe permet de formaliser des motifs, indépendamment de leur signification ou absence de signification.

Utilisation

Les expressions régulières sont interprétées par des moteurs. On utilise ces moteurs soit via une interface graphique (comme une fenêtre rechercher-remplacer), soit via une interface textuelle (comme un langage de programmation dans un script ou dans un terminal).

Le fonctionnement des moteurs à expressions régulières est assez simple. Ils lisent les fichiers caractère par caractère et évaluent chaque caractère pour essayer de trouver des correspondances :

Points de vigilance

Il existe plusieurs manières de faire fonctionner des expressions régulières. En fonction du système d’exploitation ou du logiciel dans lequel on les utilise, le moteur peut changer et la syntaxe avec.

Exemple 1 : dans les outils basés sur .NET (Microsoft), \b (le marqueur de début ou fin de mot) ne fonctionne pas avec les caractères accentués (pas de support Unicode).

Exemple 2 : selon l’environnement logiciel, la capture des groupements peut s’écrire \1 ou $1.

Exemple 3 : le comportement par défaut des expressions régulières dépend du moteur. La plupart du temps, elle seront « gourmandes » (le moteur essaye de trouver la correspondance la plus longue) et sensibles à la casse (le moteur distingue les lettres capitales de celles en bas de casse). Mais ceci peut varier d’un environnement à l’autre.

Il faut donc se renseigner sur le comportement des expressions régulières dans un environnement donné, tester ce comportement, et rester vigilant.

Syntaxe

La plus petite expression

a : le caractère a.

. : un unique caractère quelconque.

Exemple : pl.t correspond à « plat » ou « plot » mais pas « plaît » ou « planet ».

Concaténation et union

ø : lorsque deux expressions ne sont séparées par aucun caractère, elles forment une nouvelle expression par concaténation.

Exemple : ab correspond à « ab ».

| : l’une ou l’autre des expressions séparées par la barre droite. (On parle d’union car les résultats sont compris dans l’union des deux ensembles définis de part et d’autre de la barre droite.)

Exemple : a|b correspond à « a » ou « b ».

Classes

[] : un caractère parmi ceux entre crochets.

Exemple : [a7,] correspond à « a », « 7 » ou « , ».

[a-z] : un caractère parmi ceux définis dans l’intervalle entre crochets. Seuls certains intervalles sont prédéfinis, comme les chiffres et les lettres.

Exemple : [0-9] correspond à un chiffre entre 0 et 9 ; c’est l’équivalent de [0123456789].

[^] : un caractère n’appartenant pas à l’ensemble défini entre crochets.

Exemple : [^aeiouy] correspond à « b » mais pas « a ».

La classe « inversée » [^] est un outil extraordinairement efficace dès qu’on est en présence de paires de caractères servant à délimiter quelque chose : des guillemets "", des tabulations \t, des balises <a></a>… En effet, quel que soit le contenu entre les deux délimiteurs, il ne peut inclure de délimiteur, par définition ! Il n’est alors pas nécessaire de chercher comment formaliser le contenu, il suffit de le définir comme étant « tout sauf le délimiteur ». On peut ainsi écrire "[^"]+" pour trouver toutes les phrases entre guillemets, par exemple.

Classes prédéfinies

\w = [a-zA-Z_0-9] : un caractère alphanumérique.

\W = [^a-zA-Z_0-9] : un caractère non alphanumérique.

\d = [0-9] : un chiffre décimal.

\D = [^0-9] : un caractère n’étant pas un chiffre décimal.

\s = [ \t\n\x0B\f\r] : un caractère d’espacement (notamment espace , tabulation \t, retour chariot \n).

Quantificateurs

Les quantificateurs sont des caractères spéciaux qui s’appliquent à ce qui les précède immédiatement pour préciser une quantité.

? : ce qui précède est présent zéro ou une fois.

Exemple : toto? correspond à « tot » ou « toto » mais pas « totoo ».

* : ce qui précède est présent zéro ou plusieurs fois.

Exemple : toto* correspond à « tot », « toto », « totoo », « totooo »…

+ : ce qui précède est présent une ou plusieurs fois.

Exemple : toto+ correspond à « toto », « totoo », « totooo » mais pas « tot ».

{n}: ce qui précède est présent exactement n fois.

Exemple : a{3} correspond à « aaa » mais pas « aa ».

{n,m}: ce qui précède est présent entre n et m fois.

Exemple : a{2,4} correspond à « aa » ou « aaa » mais pas « a ».

{n,}: ce qui précède est présent au moins n fois.

Exemple : a{3,} correspond à « aaa », « aaaa »…

Groupement

() : groupement d’une expression.

Les groupements ont deux utilités.

  1. Isoler des parties d’une expression pour restreindre ou étendre le champ d’application d’un caractère spécial.

Exemple 1 : (a|b)c correspond à « ac » ou « bc ». Les ensembles de part et d’autre de la barre droite | sont restreints à la parenthèse (ici le « choix » est entre a et b, pas entre a et bc).

Exemple 2 : (01)+ correspond à « 01 », « 0101 », « 010101 », etc. (le + s’applique au motif entre parenthèses).

  1. Capturer des correspondances. Les groupements capturés peuvent être utilisés dans une expression régulière de remplacement.

\n (ou $n suivant le logiciel) : le énième groupement capturé.

\0 (ou $0 suivant le logiciel) : la correspondance entière.

Exemple :

Bègles 33130
Bordeaux 33800

Si on cherche 33(\d{3}) :

  • dans Bègles 33130, \1 correspond à « 130 », 0 correspond à « 33130 » ;
  • dans Bordeaux 33800, \1 correspond à « 800 », 0 correspond à « 33800 ».

Caractères spéciaux

^ : début de ligne.

Exemple : ^a correspond à « a » en début de ligne mais pas « ba ».

$ : fin de ligne.

Exemple : a$ correspond à « a » en fin de ligne mais pas « ab ».

\b : position située entre un caractère alphanumérique (\w, par exemple une lettre ou un chiffre) et un caractère non alphanumérique.

Exemple : \ba correspond à « a » en début de mot mais pas en milieu ou en fin de mot (le premier « a » de « amant » mais pas le deuxième).

On définit souvent \b comme la position de début ou fin de mot. Mais les expressions régulières ne comprennent pas le concept de « mot ». \b correspond en fait à la frontière entre un caractère appartenant à la classe prédéfinie \w et un autre caractère n’y appartenant pas. Ceci inclut les positions de début ou fin de mot, mais ne s’y limite pas. Ainsi, \b\d correspond à un chiffre en « début de mot » : cela pourrait être par exemple le premier chiffre d’un numéro de téléphone.

Échappement

Un caractère normalement utilisé dans la syntaxe peut être précédé d’une barre oblique inverse \ pour être interprété littéralement.

Exemple : a\+b correspond à « a+b » et pas à « aaaaab ».

Les caractères entre crochets (classe) sont toujours interprétés littéralement, il n’y a donc pas besoin de les échapper.

Exemple : [a+] correspond à « a » ou « + ».

Lookarounds

Les lookarounds testent la présence ou l’absence d’un motif juste avant ou juste après le motif cherché.

(?=motif) : assertion positive avant (positive lookahead), vraie si le motif est vérifié.

Exemple : a(?=5) correspond à a5b mais pas à ab5.

(?!motif) : assertion négative avant (negative lookahead), vraie si le motif échoue.

Exemple : mascar(?!p) correspond à mascara mais pas à mascarpone.

(?<=motif): assertion positive arrière (positive lookbehind), vraie si le motif est vérifié.

Exemple : (?<=re)mède correspond à remède mais pas à intermède.

(?<!motif) : assertion négative arrière (negative lookbehind), vraie si le motif échoue.

Exemple : (?<!re)mise correspond à démise mais pas à remise.

Les lookarounds sont très pratiques pour rendre une expression régulière plus discriminante en posant des contraintes sur le contexte autour du motif recherché, sans pour autant inclure ce contexte dans les résultats de recherche. En effet, lorsqu’un moteur à expressions régulières teste un lookaround, il n’avance pas : il reste en place, et n’avance au caractère suivant que lorsque la condition définie par le lookaround a été testée. Ceci facilite beaucoup les choses lorsqu’on enchaîne recherche et remplacement.

Exemple : si on cherche les occurrences de toto uniquement entre deux guillemets, il peut être intéressant d’écrire (?<")toto(?=") plutôt que "toto".