gilles.hunault@univ-angers
Un petit tuteur sur les Expressions Régulières
(a quick tutorial on regexp -- regular expressions)
Table des matières cliquable
1. Notion d'expression régulière simple
1. Notion d'expression régulière simple
Il arrive souvent qu'on ait à manipuler des groupes de valeurs numériques ou caractères. Par exemple, à partir d'un numéro de plaque d'immatriculation de voiture des années 80 comme 322 RK 75 on peut déduire le département (ici 75). De même, si H001, K002, I003...désignent des numéros de batiments et de bureaux, on est capable de dire qu'il s'agit des batiments H, I et K. Enfin, si on décortique une URL comme<a href="http://www.google.fr" class="mongoog">Google !</a> on peut trouver qu'il s'agit d'un site français (.fr). Pour décrire comment ces groupes de valeurs sont définis, on utilise des modèles (ou motifs, patterns, règles ...) qui sont en fait des chaînes de caractères dont certains caractères jouent une rôle particulier.
On nomme «expression régulière» une chaine de caractères qui définit les règles de description de ces groupes de valeur. Il serait plus correct de dire «expression rationnelle» comme en théorie des langages formels et des langages rationnels, mais l'usage veut qu'on utilise la mauvaise traduction française expression régulière pour le terme anglais regular expression, souvent compressé en le mot-valise regexp. On peut se servir des expressions régulières pour
- tester si une valeur correspond à un modèle ;
- décomposer une valeur en fonction des différentes parties du modèle ;
- remplacer (ou reformater) une valeur en fonction des différentes parties du modèle.
Le grand intérêt des expressions régulières est qu'elles permettent de programmer de façon compacte sans avoir constamment à chercher la position du début d'une sous-chaine, ni à enchainer des tests de cas ou de parcours de chaines. Cela signifie qu'en contrepartie d'un petit effort d'écriture de l'expression régulère, on obtient les chaines de caractères désirées en une seule instruction à la place d'une dizaine (ou plus) de lignes si on devait écrire le programme correspondant avec les seules fonctions classiques sur chaines de caractères. Les expressions régulières sont donc un outil puissant qu'il convient d'apprendre et de maitriser.
Il n'y a pas une définition unique et standard des caractères spéciaux ("ou métacaractère") mis en jeu dans les modèles mais plutôt des familles de définition. On distingue en général les définitions simples et les définitions avancées (ou "étendues"). Il semblerait qu'au fil des années, les définitions PERL des expressions régulières soient devenues la référence, au détriment de celle des expressions régulières POSIX et nous allons d'abord les utiliser avant de voir éventuellement d'autres définitions.
Au niveau élémentaire, toute lettre est une expression régulière (que nous abrégerons en ER) et tout chiffre est une expression régulière. Les caractéres spéciaux ^ et $ sont des métacaractères de positionnement (on dit aussi d'ancrage) : ^ signifie en début de chaine et $ en fin de chaine. Ainsi ^06 signifie «toute chaine qui commence par 06» alors que fr$ signifie «toute chaine qui se termine par les deux lettres fr».
Afin de définir un modèle (ou "règle"), les caractères ? + et * prennent une signification particulière dite de répétition :
? signifie une répétition 0 ou 1 fois du sous-modèle qui le précède
+ signifie une répétition d'au moins 1 fois du sous-modèle qui le précède
* signifie une répétition de 0 fois ou plus du sous-modèle qui le précèdeSi aucun caractère de répétition ne suit une ER, alors l'ER est considérée comme présente exactement une et une seule fois. Ainsi ABC correspond à la chaine ABC alors que AB*C correspond aux sous-modèles A, B* et C c'est-à-dire aux chaines AC, ABC, ABBC, ABBBC... c'est-à-dire un A puis un nombre quelconque (éventuel nul) de B puis un C.
Il est possible d'utiliser les parenthèses ( et ) pour indiquer un sous-modèle sur lequel doit s'appliquer la répétition. On peut aussi utiliser la barre droite | pour indiquer une alternative entre deux ou plusieurs éléments. Ainsi (AB)+ signifie AB ou ABAB ou ABABAB... c'est-à-dire une succession de AB, alors que A+B+ signifie des A suivis de B et que (A|B)+ signifie une chaine quelconque avec des A et/ou des B.
Pour signifier n'importe quel caractère (ce qui inclut les lettres, les chiffres mais aussi les symboles) on utilise le caractère . ("point") ; si on veut désigner le "vrai" point, on doit écrire l'ER \. ; les caractères [ et ] permettent de définir une classe (avec le caractère - comme indication d'étendue). Enfin, le symbole ^ en début de classe signifie qu'on ne veut pas des éléments qui sont dans la classe. Voici donc quelques exemples simples avec ces caractères pour spécifier des nombres ou des listes de nombres :
Expression régulière Signification [0-9] chaine contenant un chiffre ^[0-9]$ chaine restreinte à un seul chiffre ^[0-9]+$ chaine non vide qui contient un nombre entier (y compris 00, 000001...) ^[0-9]*$ chaine vide ou qui contient un nombre entier (y compris 00, 000001...) ^[ 0-9]*$ chaine qui contient des entiers séparés par un ou plusieurs espaces ([1-9]?[0-9])|100 un entier de 0 à 100 (sans 00, 01...) -?[0-9] un chiffre éventuellement précédé du signe moins (+|-)[0-9]+ un entier obligatoirement signé (y compris +0, -0000...) (+|-)?[0-9]+ un entier éventuellement signé (y compris +0, -0000...) Et voici quelques exemples simples avec des chaines de caractères :
Expression régulière Signification [A-Z] chaine contenant une lettre en majuscule (sans majuscule accentuée) ^[a-z]+$ chaine restreinte à un seul mot non vide en minuscule (sans lettre accentuée) ^[ATGC]+$ chaine non vide qui ne contient que les 4 lettres majuscules indiquées [A-Z][A-Z]+ chaine contenant au moins deux majuscules consécutives ^[A-Z][a-z]*$ chaine qui contient un seul mot dont l'initiale est en majuscule et le reste en minuscules ^[0-9]?[0-9]?[0-9]?[0-9][A-Z][A-Z]$ chaine composée uniquement de 1 à 4 chiffres suivi de deux lettres en majuscules ^[0-9]?[0-9]?[0-9]?[0-9] [A-Z][A-Z]$ idem mais avec un espace entre les chiffres et les lettres [A-Z][^0-9] une chaine contenant une majuscule non suivie d'un chiffre Les caractères spéciaux comme tabulation, saut de ligne... ont une représentation classique en tant qu'ER, comme par exemple \t pour tabulation, \n pour saut de ligne (Unix)... Voir notamment leur liste dans la page perlre à la rubrique Escape sequences.
2. Expressions régulières étendues
Il est clair que les ER, ce n'est pas clair ! Ou, en tous cas, pas très lisible. En particulier, la répétition quantifiée (par exemple de 8 à 20 chiffres) n'est pas simple à écrire. C'est pourquoi, avec les expressions régulières étendues, on dispose d'une syntaxe plus complète mais encore plus illisible. Elle concerne les répétitions, les ensembles typés classiques et l'extraction de sous-chaine(s).
En syntaxe étendue, la répétition peut s'écrire {min,max} si elle doit correspondre à au moins min fois et au plus max fois avec comme cas particuliers {nbf} si on veut une répétition d'exactement nbf fois, {min,} pour indiquer au moins min répétitions et {,max} pour indiquer au plus max répétitions. Les symboles précédents ? * et + en sont donc des cas particuliers définis respectivement par {0,1}, {0,} et {1,}.
Les ensembles typés classiques sont définis en POSIX par des classes avec un mot encadré de : et en PERL par des lettres précédées d'un anti-slash. Ainsi un chiffre, écrit précédemment [0-9] se note [:digit:] en POSIX et \d en PERL. PERL propose astucieusement et de façon consise, comme souvent, d'utiliser la version majuscule comme opposé de classe. Ainsi \D signifie non \d soit encore [^0-9] dans la syntaxe originale. Parmi les ensembles classiques, on trouve les mots (mais à l'américaine, sans accent) notés \w , les symboles de ponctuation, les caractères "blancs" ou "d'espace" (comme la tabulation, le retour à la ligne...) notés \s, les limites de mots, de chaines.... On pourra consulter les liens POSIX character classes et PERL Character Classes (section Character-Classes-and-other-Special-EscapesCharacter-Classes-and-other-Special-Escapes) pour le détail de ces ensembles.
Avec un peu d'entrainement, on arrive facilement à écrire des ER comme :
Expression régulière Signification \d{10} un numéro de téléphone (dix chiffres) \d{1,4}[A-Z]{2}\d{2} anciennes plaques d'immatriculation : de 1 à 4 chiffres, 2 lettres exactement, 2 chiffres exactement [A-Z]{2}-\d{3}-[A-Z]{2} nouvelles plaques : 2 lettres, un tiret, 3 chiffres, un tiret, 2 lettres La recherche de sous-modèles et l'utilisation de modèles partiels précédents (backreferences) complique encore l'écriture des ER mais rend de nombreux services. PERL utilise par défaut un mode gourmand ou glouton (greedy) qui peut être converti en un mode fainéant (lazy) via le point d'interrogation. En d'autres termes, avec le point d'interrogation on s'arrête à la première correspondance alors que par défaut on va jusqu'à la dernière correspondance.
Si on utilise des parenthèses dans une ER autour d'une répétition alors PERL mémorise les correspondances et les nomme $1,$2 alors que d'autres langages les notent \1,\2...
Par exemple l'ER ".*" utilisera le premier et le dernier symbole " comme correspondances alors que "(.*?)" utilisera le premier symbole " et le suivant comme correspondances. Ainsi, avec la chaine <table id="tableau1" class="essai"> la première ER désigne tout le texte tableau1" class="essai alors qu'avec la seconde, seul tableau1 correspond et de plus ce texte est "capturé".
3. Quelques liens pour aller plus loin
3.1 Quelques références
La notion d'ER étant transversale, on ne s'étonnera pas de la retrouver dans tous les langages de programmation, avec parfois quelques différences...
3.2 Tester en ligne des expressions régulières
Il est parfois difficile de s'y retrouver, dans les expressions régulières. Heureusement, de nombreux sites permettent de tester et de mettre au point une ER avant de l'incorporer dans un programme. On n'oubliera pas de documenter l'ER, comme dans le fameux exemple pour striphtml.
Les deux meilleurs sites (selon nos critères) que nous conseillons sont
Mais il existe bien sûr de nombreux autres sites traitant des ER, comme
3.3 Les expressions régulières dans un contexte de vérification et de remplacement silencieux
Lorsqu'on écrit des pages Web avec des formulaires et du texte à saisir, on est souvent confronté à la vérification des données saisies. Vérifier en local avec Javascript, depuis la page que la saisie correspond à ce qui était attendu évite des allers et retours au niveau du serveur. Cela permet aussi de corriger (sans le montrer explicitement) la saisie pour éviter des particularismes. Ainsi, la plupart des langages informatiques, programmés en anglais, ne considèrent pas 3,2 comme la représentation correcte de 32/10 et s'attendent à 3.2 avec le point comme séparateur décimal. Réaliser cela avec une expression régulière se fait bien sûr en une seule ligne, sans compter la vérification "rapide" d'une chaine correspondant à une adresse mail via [a-z][-._a-z0-9]+@[a-z][-._a-z0-9]+\.[a-z]{2}, plus facile à paraphraser via la colorisation [a-z][-._a-z0-9]+@[a-z][-._a-z0-9]+\.[a-z]{2}.
Dans le cadre de l'évaluation de réponses textuelles, les expressions régulières permettent de "tolérer" des "petites fautes". Ainsi, mettre vert? comme couleur de réponse accepte les deux chaines ver et vert puisque vert? se lit vert? et signifie « les trois lettres ver puis éventuellement la lettre t». De même, mettre ball?l?on accepte les trois écritures balon, ballon et balllon. Pour la langue française et les h muets, les ambiguités i/y, les expressions régulières permettent d'accepter h?ier pour hier, ps[yi]ch?ologie?/(ph|f)[yi]lo[sz]o(ph|f)ie?, pour psychologie/philosophie, etc. Cela peut aussi servir pour la recherche de noms propres étrangers quand on ne sait pas où sont les h, w et y comme pour les personnes nommées Warshowsky, Schwarzkopf, ЧебЫШӛВ (Tchebychev/Tshebitcheff)...
Retour à la page principale de (gH)