Expressions régulières
Une expression régulière (regex) est un motif (pattern) destiné à identifier des chaînes de caractères. Donc, étant donné une regex, une chaine de caractères correspond (match) ou ne correspond pas à cette regex.
Concept issu de travaux théoriques en neuroscience (Stephen Kleen, 1956),
les regex sont implémentées informatiquement pour la première fois par Ken Thompson dans le premier éditeur de texte d’Unix (ed),
pour rechercher et substituer des portions de texte.
Depuis, elles sont devenues une abstraction à part entière,
apparessant un peu partout en informatique :
de nombreuses commandes en ligne les comprennent,
nombre de configurations logicielles les utilisent dans leur formulation,
et la plupart des langages de programmation permettent au développeur de les exploiter.
Le formalisme concis des regex, bien que concourant à leur réputation ésotérique, offre des possibilités élégantes et puissantes de traitement de texte.
1. Aperçu
Une regex permet d’identifier finement un ensemble de chaînes de caractères.
La regex est elle-même une chaîne de caractères.
Par exemple, la regex defg représente toute chaîne de caractères contenant la chaîne "defg", comme par exemple :
abcdefg defghi abcdefghi defg
Par contre, les chaînes ci-dessous ne s’idenfient pas à la regex defg :
def # ne contient pas g efg # ne contient pas d ddeeffgg # ne contient pas def gfed # ne contient pas de
La commande grep
La commande Unix grep affiche les lignes d’un (ou plusieurs) fichier s’identifiant à la regex passée en argument.
Par exemple :
$ grep net /etc/protocols
affiche toutes les lignes du fichier /etc/protocols s’identifiant à la regex net.
Des options peuvent modifier le comportement de grep.
Par exemple -i signifie de ne pas différencier les minuscules des majuscules :
$ grep -i internet /etc/protocols
De même, -v demande à grep d’afficher les lignes ne correspondant pas à la regex.
Jocker, quantificateur, délimiteur
Quelques caractères jouent un rôle particulier, différent de leur propre représentation.
Par exemple . ne représente pas le caractère point.
Historique
Il existe plusieurs types de regex (avec une compatibilité ascendante ) : les regex de base, les regex étendues et les regex de Perl, apparues dans cet ordre chronologique.
Les regex de base sont apparues à la naissance d’Unix. C’est le standard historiquement supporté par UNIX et ses commandes depuis l’origine.
Les regex étendues complètent les regex de base, en introduisant de nouveaux caractères spéciaux, pour plus de fonctionnalités. Les regex étendues sont un sur-ensemble des regex de base.
Même chose avec les regex de Perl, qui complètent les regex étendues. Perl est un langage de script créé spécialement pour traiter efficacement les chaînes de caractères, et dont le champ d’action s’est élargi au fil du temps, au point de devenir un langage généraliste (manipulant efficacement les regex).
2. Regex de base
C’est le socle de base.
| car. | type | desc. |
|---|---|---|
|
jocker |
1 caractère quelconque |
|
délimiteur |
début de ligne |
|
délimiteur |
fin de ligne |
|
quantificateur |
caractère précédent répété 0 ou plusieurs fois |
|
modificateur |
litéralise le caractère suivant |
Le joker .
Le caractère "." est un jocker : il représente un caractère quelconque.
Les chaînes suivantes :
defg dafg dbfg dcfg ddfg d.fg abcd fghi
s’identifient à la regex d.fg,
au contraire des chaînes ci-dessous :
dfg # pas de caractère entre d et f dxyfg # trop de caractères en d et f abcd fghi # trop de caractères en d et f
qui ne s’y identifient pas.
Le modificateur \
Le caractère "\" agit sur le caractère qui le suit dans la regex :
-
si ce caractère est particulier, alors il perd son super-pouvoir, et devient un caractère ordinaire,
-
si ce caractère est ordinaire, alors il le reste (le modificateur n’a donc pas d’effet dans ce cas).
Les délimiteurs ^ et $
Le caractère ^ en début de regex matérialise le début de la chaîne.
Symétriquement, le caractère $ en fin de regex matérialise la fin de la chaîne.
Ainsi :
-
^aidentifie toute chaîne commençant par"a", -
b$identifie toute chaîne finissant par"b".
Le quantificateur *
Le caractère "*" dans une regex signifie que le caractère qui le précède dans la regex est répété 0, 1 ou plusieurs fois.
Ainsi :
-
a*signifie : 0, 1 ou plusieurs"a". -
ab*csignifie :"a"suivi de 0, 1 ou plusieurs"b", suivi de"c".
|
regex vs glob
Les shells Unix utilisent des patterns (appelé glob) pour identifier des ensembles de noms de fichiers. glob et regex ont le même objectif, mais sont différents et incompatibles. Les globs admettent seulement 2 jokers : Ainsi :
|
3. Regex étendues
Elles introduisent de nouveaux caractères spéciaux pour plus de fonctionnalités.
| car. | type | desc. |
|---|---|---|
|
alternative |
soit les caractères précédents, soit les caractères suivants |
|
regroupement de caractères |
|
|
quantificateur |
caractère précédent répété au moins une fois |
|
quantificateur |
caractère précédent répété au plus une fois |
|
quantificateur |
caractère précédent répété n fois |
|
quantificateur |
caractère précédent répété entre n et m fois |
|
quantificateur |
caractère précédent répété au plus n fois |
|
quantificateur |
caractère précédent répété au moins n fois |
|
alternative |
1 caractère parmi ceux énumérés |
|
alternative |
1 caractère dans l’intervalle |
|
alternative |
1 caractère dans les intervalles |
|
alternative |
1 caractère en dehors des intervalles |
Pour utiliser les regex étendues avec grep, il faut spécifier l’option -E.
Ou bien utiliser la commande egrep.
|
L’alternative |
-
a|bsignifie :aoub. -
ab|cdesignifie :aboucde.
Ainsi, les chaînes "viability", "undeavour", "stabilate" et "cadeau"
s’identifient à la regex abil|dea.
Le groupement ( )
Les parenthèses regroupent plusieurs caractères. Ainsi :
-
(ab)*signifie : 0 ou plusieurs occurrences deab -
a(b|cd)esignifie :a, suivi deboucd, suivi dee.
Ainsi, les chaînes "stabilate" et "cadeau"
s’identifient à la regex a(bil|de)a, mais pas "viability" ni "undeavour".
Le quantificateur +
Au moins 1 fois ce qui précède. Ainsi :
-
a+signifie : 1 ou plusieurs"a" -
ab+csignifie donc :"a", suivi de 1 ou plusieurs"b", suivi de"c"
a+ est donc un raccourci pour aa*.
|
Le quantificateur ?
Au plus 1 fois ce qui précède. Ainsi :
-
a?signifie : 0 ou 1"a" -
ab?csignifie donc :"a"suivi de 0 ou 1"b", suivi de""
Les quantificateurs { }
Au plus et au moins. Ainsi :
-
a{n}signifie"a"répété n fois -
a{n,m}signifie"a"répété entre n et m fois -
a{,n}signifie"a"répété au plus n fois -
a{n,}signifie `
|
Sont donc équivalents :
|
L’alternative [ ]
Elle permet de définir 1 caractère unique pris dans un ensemble donné.
|
|
|
|
|
|
Captures
En réalité, les parenthèses groupent, mais aussi capturent la portion qu’elles délimitent.
La capture est mémorisée, dans le but d’être utilisée plus tard.
On récupère la capture via \1.
Exemple :
-
a|bidentifie le caractère"a"ou le caractère"b" -
(a|b)capture le caractère qui s’est identifié (donc soit"a", soit"b") -
\1sera donc soit"a", soit"b"
Exemple complet :
-
^(a|b)\1$signifie donc soit"aa", soit"bb", mais pas"ab"ni"ba".
Plusieurs captures sont possibles, et dans ce cas, on les récupère séquentiellement via \1, \2, \3, etc.
Exemple :
-
(a|i)\1(r|s)\2identifie toutes les chaines contenant l’un des motifs suivant :
aarr aass iirr iiss
|
Par défaut, les quantificateurs sont gourmands,
c’est-à-dire qu’ils identifient à la chaîne la plus longue possible.
Par exemple, dans la chaine ** rien ** c cc ccc cccc mais les quantificateurs étant gourmands, la portion capturée est finalement |
4. Perl regex
Le langage Perl ajoute de nouvelles fonctionnalités aux regex étendues, dont quelques unes des plus simples sont listées ci-dessous.
Pour utiliser les regex de Perl avec grep, il faut spécifier l’option -P.
|
Les regex Perl offrent des délimiteurs supplémentaires :
|
une fontière de mot |
|
une non-fontière de mot |
Elles amènent aussi quelques alternatives :
|
un chiffre ( |
|
un non-chiffre ( |
|
un séparateur blanc ( |
|
un non-séparateur blanc ( |
|
un caractère alphanumérique ( |
|
un caractère non alphanumérique ( |
Elles offrent également des quantificateurs non gourmands :
|
0 ou plusieurs occurrences, la plus courte possible |
|
1 ou plusieurs occurrences, la plus courte possible |
Par exemple, étant donnée la chaine
"abcabc",
alors la regex (a.*c) capture
"abcabc"
alors que la regex (a.*?c) capture
"abc".