Les expressions régulières (regex ou regexp) sont extrêmement utiles pour extraire des informations d’un texte en recherchant une ou plusieurs correspondances à l’aide d’un modèle de recherche spécifique (c’est-à-dire une séquence spécifique de caractères ASCII ou Unicode). Une regex est donc faite pour effectuer des recherches dans les chaînes de caractères … et une regex est elle-même une chaîne de caractère. Par exemple, la regex [0-9]+ ans
permet de matcher l’âge dans une chaîne.
Les domaines d’application vont de la validation à l’analyse/remplacement de chaînes de caractères, en passant par la transposition de données vers d’autres formats mais aussi le web scraping. Cependant, si cet outil est très puissant, il est relativement difficile à appréhender au début car les expressions régulières peuvent prendre des formes de ce genre :
#^[a-zA-Z-]+@[a-zA-Z-]+\.[a-zA-Z]{2,6}$#
Cette expression régulière permettant, par exemple, de vérifier le format de toutes les adresses emails contenues dans un texte.
L’un des avantages des regex est qu’une fois que vous avez appris la syntaxe, vous pouvez l’utiliser dans (presque) tous les langages (JavaScript, Java, VB, C#, C/C++, Python, Perl, Ruby, Delphi, R, Tcl et bien d’autres) avec quelques légères distinctions pour les fonctionnalités les plus avancées et les versions de syntaxe prises en charge par les moteurs.
Commençons par voir les fonctionalités de Bash et leurs limitations avant d’attaquer les véritables expressions régulières.
Les motifs du shell contiennent :
Signification | bash |
---|---|
joker (1 caractère) | ? |
joker (plusieurs caractères, éventuellement zéro) | * |
caractère parmi … | […] |
caractère sauf … | [!…] |
caractères ? * | \? * |
La commande suivante permet de lister tous les fichiers dont l’extension est “.c”.
$ ls *.c
Question Donnez une commande qui liste tous les fichiers .c ou .h.
Question Donnez une commande pour lister tous les fichiers dont l’avant dernier caractère n’est pas le point “.”.
Pour tester, vous pouvez créer des fichiers vides (dans un répertoire temporaire) avec la commande touch:
$ touch fichier1.c Fichier2.h FICHIER3.jpg
Question Créez les fichiers suivants dans un répertoire temporaire :
$ touch test.jpg test.jpeg index.html test-1.1.jpg test-1.2.jpg tux.png
Certain shells, comme bash, ont des motifs plus perfectionnés. Utilisez la commande suivante pour activer les motifs étendus de bash :
$ shopt -s extglob
Question Vous pouvez maintenant utiliser un nouveau type de motif : (reportez vous à la section pertinente du manuel de bash pour plus de détails)
signification | bash |
---|---|
chaine ne correspondant à aucun motif … | !(…|…|…|…) |
Le résultat de la commande suivante n’est pas d’afficher la liste des fichiers, sauf ceux dont l’extension est “.jpeg” ou “.jpg”. Testez la commande et expliquez ce que la commande doit afficher.
$ ls *!(.jpeg|.jpg)
???
Question Quel motif shell peut on utiliser pour obtenir la liste de tous les fichiers sauf ceux dont l’extension est .jpg ou .jpeg ?
Les motifs shell sont uniquement utilisables en shell ce qui limite très largement leur utilisation dans d’autres contextes. Pour palier ce probleme, il existe les expression régulières.
Nous allons voir les bases des regex avec la commande grep
. grep
est une commande POSIX qui permet d’afficher les lignes d’un fichier contenant une chaine reconnue par une expression régulière (regex).
L’utilisation courante en ligne de commande est
$ grep -E 'REGEX' FICHIER1 FICHIER2 ...
Les guillemets autour de l’expression régulière ne sont pas toujours obligatoires. Ils évitent au shell de considérer l’expression régulière comme un motif du shell. D’autre part, si aucun fichier n’est donné, grep utilisera l’entrée standard à la place.
La regex “aa” correspond aux chaines de caractères qui contiennent des suites de deux lettres “a”.
Question Testez cette regex en appelant la commande grep sur le fichier test.txt contenant les six lignes suivantes:
aa
aaaa
aaaaaa
aaa
bbbbb
CECI EST LA FIN DU FICHIER...
Question Quelles sont les lignes reconnues par l’expression régulière ?
Relancez la commande en ajoutant l’argument –color à grep :
$ grep -E --color 'REGEX' FICHIER
^
et $
Les ancres de début et de fin de chaîne, respectivement ^
et $
, représentent, comme leur nom l’indique, le début et la fin de la chaîne. Par exemple:
^Début
correspond à toute chaîne de caractères qui commence par “Début”fin$
correspond à toute chaîne de caractères qui se termine par “fin“
^la fin$ correspond exactement à la chaîne de caractères “la fin“on
correspond à toute chaîne de caractères qui a “on” à l’intérieurQuestion Créer un fichier test.txt contenant les lignes suivantes, tester ensuite ces regex avec ou sans ^
et $
.
Début
c'est le début
c'est le Début
c'est la fin
la fin ?
oui, la fin
la fin c'est sur
Par exemple,
$ grep --color -E "^Début" test.txt
$ grep --color -E "Début$" test.txt
$ grep --color -E "^Début$" test.txt
Imaginons maintenant que nous voulons rechercher, dans le texte, les mots “Début” et “fin”, c’est à dire le mot “Début” OU le mot “fin” : nous devrons alors utiliser la barre verticale |
. Ainsi la regex suivante sélectionnera toutes les occurrences de Début et fin :
Début|fin
Il est aussi possible de sélectionner les occurrences du mot “Début” se trouvant au début du texte et du mot “fin” se trouvant à la fin, ce qui revient à fusionner, en les séparant par une barre verticale, les 2 regex vues précédemment. Voici donc la regex correspondant :
^Début|fin$
Question Tester cette regex sur le fichier test.txt
Plus globalement,
a(b|c)
correspond à une chaîne de caractères qui possède un “a” suivi de “b” ou “c”a[bc]
exactement même résultat, toutes les lettres entre crochet sont une alternative, retourne donc les chaînes de caractères correspondant à “ab” ou “ac“Passons maintenant à des regex un peu plus complexes, et beaucoup plus puissantes, en nous attardons sur les ensembles de caractères.
Les classes permettent de recherche entre plusieurs caractères différents, elles donnent des alternatives. Les intervales sont des classes un peu spéciales puisqu’elles permettent d’énumérer une certaine palette de chiffre ou de lettres. Par exemple, tous les chiffres de 0 à 5, ou toutes les lettres de a à i, sans les énumérer une par une.
Quelques particularités : dans un classe, le tiret “-“ sert de délimiteur, donc si on veut l’inclure en tant que caractère, on doit le placer en fin de classe (ou au début). Par ailleurs, le crochet fermant “]” délimite aussi la fin de le classe, il faudra donc l’échapper par un antislash. En revanche, les autres métas-caractères ne comptent pas dans les classes. On ne les échappe pas. Cette classe [0-9a-z?+*{}.] correspond donc à un chiffre, une lettre, un point d’interrogation, un point, un plus…
Signe | Signification | Exemple |
---|---|---|
[ ] | classe de caractères | /gr[oai]s/ (gros, gras ou gris) |
[ - ] | intervalle de classe | /n°[0-9]/ (n°1, n°2, […] n°9) |
[^ ] | classe à exclure | /h[^3-9]/ (h1 et h2 uniquement) |
Question Créer le fichier text.txt suivant :
Bonjour et au revoir ! Je suis John Doe, j'ai 27 ans, j'habite en France et travaille depuis que j'ai 20 ans. Ma passion : écrire des mots, mits, mets, mats, mat... Pour me contacter, vous pouvez envoyer un email à contact@johndoe.fr ou contact@johndoe.com ou bien m'appeler au 06 07 08 09 10. Vous pouvez aussi aller voir mon blog à l'adresse johndoe-blog.fr. Ceci est la fin
Maintenant, nous voulons chercher dans notre texte, les mots ‘mots’, ‘mats’ et ‘mits’. Nous pourrions très bien utiliser cette regex :
mots|mats|mits
Question Trouver la regex qui va bien et qui utilise une classe
Cette regex peut être expliqué par la phrase suivante : “Sélectionne les parties du texte où il y a un m, suivi d’un o ou d’un a ou d’un i, suivi d’un t, suivi d’un s.”
Les ensembles de caractères permettent aussi d’exclure des caractères grâce à l’accent circonflexe ^.
Question Tester la regex suivante, que fait-elle ?
m[^oai]ts
Enfin, imaginons que nous voulons sélectionner tous les mots commençant par un m, suivi de n’importe quelle lettre, suivi d’un t, suivi d’un s. La regex valable qui nous viendrait à l’esprit serait une regex de ce type :
m[abcdefghijklmnopqrstuvwxyz]ts
Question Donner la regex avec intervalle pour réaliser le même filtre que m[abcdefghijklmnopqrstuvwxyz]ts
.
Voici quelques exemples d’intervalles souvent utilisés:
Intervalle | Equivalent | Traduction |
---|---|---|
[a-z] | [abcdefghijklmonpqrstuvwxyz] | Lettres minuscules de a à z |
[A-Z] | [ABCDEFGHIJKLMNOPQRSTUVWXYZ] | Lettres majuscules de A à Z |
[0-9] | [0123456789] | Chiffres de 0 à 9 |
[a-z0-9] | [abcdefghijklmonpqrstuvwxyz0123456789] | Lettres minuscules de a à z ou chiffres de 0 à 9 |
Les classes abrégées permettent, comme les classes “normales”, d’avoir de nombreuses possibilités. Elles n’apportent rien de plus en fonctionnalité que les classes normales, mais elles permettent d’écrire tout ça bien plus vite, ce sont des raccourcis ! Que diriez vous si vous pouviez taper \w à la place de [0-9a-zA-Z_] ?
Raccourci | Signification |
---|---|
\d |
Indique un chiffre. revient exactement à taper [0-9] |
\D |
Indique ce qui n’est PAS un chiffre. revient à taper [^0-9] |
\w |
Indique un caractère alphanumérique ou un tiret de soulignement. Cela correspond à taper [a-zA-Z0-9_] |
\W |
Indique ce qui n’est PAS un caractère alphanumérique ou un tiret de soulignement. revient à taper [^a-zA-Z0-9_] |
\t |
Indique une tabulation |
\n |
Indique une nouvelle ligne |
\r |
Indique un retour chariot |
\s |
Indique un espace blanc (correspond à \t \n \r) |
\S |
Indique ce qui n’est PAS un espace blanc (\t \n \r) |
. |
Le point indique n’importe quel caractère ! Il autorise donc tout ! |
Voila quelques exemple de regex utilisant les classes et intervalles:
*
, +
, ?
et {}
Nous venons de voir que les classes et les intervalles de caractères permettent de définir de manière très simple les valeurs possible d’un caractère. Mais qu’en est-il si l’on définir les mêmes valeurs possibles pour plusieurs caractères ? Par exemple, si l’on veut sélectionner les parties du texte où il y a un m, suivi d’un a, suivi de 3 fois n’importe quelle lettre minuscule, est-on obligé d’utiliser une regex de ce type :
ma[a-z][a-z][a-z]
Il existe une méthode plus simple qui consiste à utiliser les quantificateurs : ce sont des caractères qui indiquent le nombre de répétition du caractère ou de la suite de caractère qui les précèdent. Le quantificateur, dans sa forme explicite, peut s’écrire de 4 façons :
{min,max}
: le nombre de répétition varie entre la valeur minimale et la valeur maximale incluses{min,}
: le nombre de répétition varie entre la valeur minimale incluse et l’infini{,max}
: le nombre de répétition varie entre 0 et la valeur maximale incluse{nombre}
: le nombre de répétition correspond au nombre marqué entre les accoladesPar exemple, la regex [a-zA-Z]{6}
permet de sélectionner les parties du texte où il y a 6 lettres consécutives.
Et [0-9]{2,4}
permet de sélectionner les parties du texte où il y a entre 2 et 4 chiffres consécutifs.
Comme pour les ensembles de caractères, il existe aussi des quantificateurs préconçus. En voici la liste :
Quantificateur | Signification | Exemple |
---|---|---|
? |
0 ou 1 fois | /bue?no/ (buno, ou bueno) |
+ |
1 ou plus | /bue+no/ (bueno, bueno, bueeeeeeno…) |
* |
0, 1 ou plus | /bue*no/ (buno, bueno, bueeeeeeno…) |
( ) |
permet d’appliquer répétition sur plusieurs signes | /Ay(Ay)*/ ( Ay, AyAy, AyAyAyAyAyAy…) |
{ } |
préciser le nombre de répétitions | /Ay(Ay){3}/ (AyAyAyAy) /Ay(Ay){1-4}/ (AyAy, AyAyAy […] AyAyAyAyAy)* /Ay(Ay){3,}/ (AyAyAyAy ; AyAyAyAyAy ; etc) |
Question Trouver et tester la regex utilisant les quantificateurs et qui remplace la regex suivante ma[a-z][a-z][a-z]
Plus d’exemples :
abc*
correspond à une chaîne de caractères qui possède “ab” suivi de zéro ou plusieurs “c”abc+
correspond à une chaîne de caractères qui possède “ab” suivi d’un ou plusieurs “c“abc?
correspond à une chaîne de caractères qui possède “ab” suivi de zéro ou un “c“abc{2}
correspond à une chaîne de caractères qui possède “ab” suivi de 2 “c“abc{2,}
correspond à une chaîne de caractères qui possède “ab” suivi de 2 (ou plus) “c“abc{2,5}
correspond à une chaîne de caractères qui possède “ab” suivi de 2 à 5 “c“a(bc)*
correspond à une chaîne de caractères qui possède un “a” suivi de zéro ou plusieurs séquences “bc“a(bc){2,5}
correspond à une chaîne de caractères qui possède un “a” suivi de 2 à 5 copies de la séquence “bc“()
Nous l’avons vu, les parenthèses ()
permettent de grouper plusieurs signes afin d’appliquer une condition, une répétition etc. Cependant, les parenthèses possèdent une autre fonction : elles sont capturantes.
Qu’est-ce que cela veut dire ? Ça veut dire qu’une expression mise entre parenthèse est automatiquement placée dans une variable à laquelle ont peut faire référence ailleurs.
On peut faire référence aux expressions capturées à deux endroits :
Dans la regex elle-même, cela s’appelle une backreference ou référence arrière. On peut ainsi sélectionner les palindromes de trois lettres (mots qui se lisent indifféremment de gauche à droite ou de droite à gauche) avec cette expression :
(\w)\w\1
Dans cette expression, (\w)
sélectionne tout caractère alphanumérique et capture ce caractère, suivi d’un autre \w
caractère puis du caractère précédemment capturé (donc l’expression entre parenthèses, soit la première lettre sélectionnée). Le \1
permet de faire référence au premier groupe capturé.
Question Tester cette derniere regex ((\w)\w\1
) avec les chaines de caractères suivantes: “SOS”, “gag”, “SOOS”, “gagag”; mais également avec “SOS sdfsdf gag”
Il peut y avoir plusieurs backreferences dans une même expression, la première est indiquée par \1, la seconde \2 et ainsi de suite.
La regex “(aa)+” devrait correspondre à l’ensemble des suites de “a” dont la taille est paire. Testez cette regex en appelant la commande grep sur le fichier test.txt contenant les six lignes suivantes.
aa
aaaa
aaaaaa
aaa
bbbbb
CECI EST LA FIN DU FICHIER...
Question Quelles sont les lignes reconnues par l’expression régulière ?
Question Relancez la commande en ajoutant l’argument –color à grep :
$ grep --color 'REGEX' FICHIER
Question Expliquez pourquoi la ligne aaa est reconnue par l’expression régulière ?
Question Corrigez la regex pour qu’elle reconnaisse seulement les lignes qui contiennent exactement une suite de a de longueur paire. (Vous devrez utiliser “^” et “$”.)
Question Créer un répertoire essai-grep dans votre home directory. Dans ce répertoire créer les fichiers suivants: tomate, poire, pomme, cerise, Fraise, fraise, courgette, POMME3, afraise.
Question Filtrer la sortie de la commande ls (sortie de la commande ls redirigée vers grep avec ls * | grep -E --color 'regex'
) avec les critères sur leur nom suivant:
Le fichier american-english contient les mots courants de la langue anglaise (avec accords et conjugaisons), à raison de un mot par ligne. Le mot barbecued contient les lettres a, b, c et d dans l’ordre.
Question Y’a t’il des mots anglais dans le fichier qui contiennent les lettres a, b, c, d et e dans l’ordre ? (Les autres lettres peuvent être quelconques.) Donnez l’expression régulière que vous utilisez.
Questions Donner une expression régulière qui liste tout les mots contenant une lettre doublée, comme “blossom” ou “zoom”.
Indice : il faudra utiliser un regroupement et une référence…
Question Donnez une expression régulière qui donne les mots avec lettre doublée qui ne sont pas des noms propres. (C’est à dire qui ne commencent pas par une lettre majuscule…) Ces mots contiennent par exemple llama, gamma, zoo.
Question Y’a t’il des mots anglais contenant une même lettre 6 fois, qui n’est pas le “s”? Donnez la ligne de commande que vous utilisez.
Il est parfois plus pratique d’utiliser plusieurs appels à la commande grep plutôt que de trouver une unique expression régulière. On peut utiliser pour ceci une redirection afin de chercher (avec grep) dans le résultat d’un appel précédent à grep. Par exemple, on peut obtenir la liste des mots qui contiennent à la fois 2 n et p, dans n’importe quel ordre :
$ grep 'nn' american-english | grep 'pp'
Question Cherchez la liste des mots anglais qui satisfont les deux conditions suivantes : (1) il commencent et terminent par la même lettre, (2) il contiennent une lettre répétée au moins 5 fois dans le mots (la première et dernière peuvent compter).
On peut également utiliser grep sur le résultat d’une autre commande arbitraire.
$ ps aux
La commande précédente affiche la liste de tous les processus existant, avec une multitude d’informations.
Question Donnez une commande pour afficher la liste des processus des navigateurs internet, c’est à dire dont le nom contient firefox, chromium, safari, etc. (ajoutez le nom de votre navigateur favori).
Question Améliorez votre ligne de commande pour ne pas afficher les lignes qui ne servent à rien. (Vous pouvez notamment utiliser l’argument -v de grep…)
Afin d’apprendre à construire un regex efficacement, on ne peut oublier un concept fondamental: les indicateurs.
Une expression rationnelle vient généralement avec cette forme /abc/, où le motif de recherche est délimité par deux barres obliques /. A la fin, nous pouvons spécifier un indicateur avec ces valeurs (nous pouvons également les combiner):
Les quantificateurs (* + {}) sont des opérateurs généreux. Ils étendent la correspondance aussi loin que possible à travers le texte fourni.
Par exemple, <.+> correspond à <div>simple div</div> dans Ceci est un test d’un <div>simple div</div>. Afin d’attraper uniquement la balise div, nous pouvons utiliser un ? pour le rendre plus sélect: <.+?> correspond à tout caractère une ou plusieurs fois inclus entre < et > -> Je teste !
Notez qu’une meilleure solution devrait éviter l’utilisation de . en faveur d’un regex plus strict: <[^<>]+> correspond à n’importe quel caractère sauf < ou > une ou plusieurs fois inclus entre < et > -> Je teste !