Une introduction à Python 3

Image non disponible


précédentsommairesuivant

13. Les expressions régulières

Les expressions régulières(51) fournissent une notation générale très puissante permettant de décrire abstraitement des éléments textuels. Il s'agit d'un vaste domaine dont nous ne proposons qu'une introduction.

13-1. Introduction

Dès les débuts de l'informatique, les concepteurs des systèmes d'exploitation eurent l'idée d'utiliser des métacaractères permettant de représenter des modèles généraux. Par exemple, dans un shell Linux ou dans une fenêtre de commande Windows, le symbole *(52) remplace une série de lettres, ainsi *.png indique tout nom de fichier contenant l'extension png. Python offre en standard les modules glob et fnmatch pour utiliser la notation des métacaractères.

Depuis ces temps historiques, les informaticiens(53) ont voulu généraliser ces notations. On distingue classiquement trois stades dans l'évolution des expressions régulières :

  • les expressions régulières de base (BRE, Basic Regular Expressions) ;
  • les expressions régulières étendues (ERE, Extended Regular Expressions) ;
  • les expressions régulières avancées (ARE, Advanced Regular Expressions).

Trois stades auxquels il convient d'ajouter le support de l'encodage Unicode.

Python supporte toutes ces évolutions.

13-2. Les expressions régulières

Une expression régulière(54) se lit (et se construit) de gauche à droite. Elle constitue ce qu'on appelle traditionnellement un motif de recherche(55).

13-2-1. Les expressions régulières de base

Elles utilisent six symboles qui, dans le contexte des expressions régulières, acquièrent les significations suivantes :

  • le point . représente une seule instance de n'importe quel caractère sauf le caractère de fin de ligne. Ainsi l'expression t.c représente toutes les combinaisons de trois lettres commençant par « t » et finissant par « c », comme tic, tac, tqc ou t9c, alors que b.l.. pourrait représenter bulle, balai ou bêler.
  • la paire de crochets [ ] représente une occurrence quelconque des caractères qu'elle contient. Par exemple [aeiouy] représente une voyelle, et Duran[dt] désigne Durand ou Durant. Entre les crochets, on peut noter un intervalle en utilisant le tiret. Ainsi, [0-9] représente les chiffres de 0 à 9, et [a-zA-Z] représente une lettre minuscule ou majuscule. On peut de plus utiliser l'accent circonflexe en première position dans les crochets pour indiquer le contraire de … Par exemple [^a-z] représente autre chose qu'une lettre minuscule, et [^'"] n'est ni une apostrophe ni un guillemet.
  • l'astérisque * est un quantificateur, il signifie aucune ou plusieurs occurrences du caractère ou de l'élément qui le précède immédiatement.
    L'expression ab* signifie la lettre a suivie de zéro ou plusieurs lettres b, par exemple ab, a ou abbb et [A-Z]* correspond à zéro ou plusieurs lettres majuscules.
  • l'accent circonflexe ^ est une ancre. Il indique que l'expression qui le suit se trouve en début de ligne.
    L'expression ^Depuis indique que l'on recherche les lignes commençant par le mot Depuis.
  • le symbole dollar $ est aussi une ancre. Il indique que l'expression qui le précède se trouve en fin de ligne. L'expression suivante :$ indique que l'on recherche les lignes se terminant par « suivante : ».
    L'expression ^Les expressions régulières$ extrait les lignes ne contenant que la chaîne Les expressions régulières, alors que ^$ extrait les lignes vides.
  • la contre-oblique \ permet d'échapper à la signification des métacaractères. Ainsi \. désigne un véritable point, \* un astérisque, \^ un accent circonflexe, \$ un dollar et \\ une contre-oblique.

13-2-2. Les expressions régulières étendues

Elles ajoutent cinq symboles qui ont les significations suivantes :

  • la paire de parenthèses ( ) est utilisée à la fois pour former des sous-motifs et pour délimiter des sous-expressions, ce qui permettra d'extraire des parties d'une chaîne de caractères. L'expression (to)* désignera to, tototo, etc. ;
  • le signe plus + est un quantificateur comme *, mais il signifie une ou plusieurs occurrences du caractère ou de l'élément qui le précède immédiatement. L'expression ab+ signifie la lettre a suivie d'une ou plusieurs lettres b ;
  • le point d'interrogation ? troisième quantificateur, il signifie zéro ou une instance de l'expression qui le précède. Par exemple écran(s)? désigne écran ou écrans ;
  • la paire d'accolades { } précise le nombre d'occurrences permises pour le motif qui le précède. Par exemple [0-9]{2,5} attend entre deux et cinq nombres décimaux. Les variantes suivantes sont disponibles : [0-9]{2,} signifie au minimum deux occurrences d'entiers décimaux et [0-9]{2} deux occurrences exactement ;
  • la barre verticale | représente des choix multiples dans un sous-motif. L'expression Duran[dt] peut aussi s'écrire (Durand|Durant). On pourrait utiliser l'expression (lu|ma|me|je|ve|sa|di) dans l'écriture d'une date.

Dans de nombreux outils et langages (dont Python), la syntaxe étendue comprend aussi une série de séquences d'échappement :

  • \ : symbole d'échappement ;
  • \e : séquence de contrôle escape ;
  • \f : saut de page ;
  • \n : fin de ligne ;
  • \r : retour-chariot ;
  • \t : tabulation horizontale ;
  • \v : tabulation verticale ;
  • \d : classe des nombres entiers ;
  • \s : classe des caractères d'espacement ;
  • \w : classe des caractères alphanumériques ;
  • \b : délimiteurs de début ou de fin de mot ;
  • \D : négation de la classe \d ;
  • \S : négation de la classe \s ;
  • \W : négation de la classe \w ;
  • \B : négation de la classe \b.

13-3. Les expressions régulières avec Python

Le module re permet d'utiliser les expressions régulières dans les scripts Python. Les scripts devront donc comporter la ligne :

 
Sélectionnez
import re

13-3-1. Pythonismes

Le module re utilise la notation objet. Les motifs et les correspondances de recherche seront des objets de la classe re auxquels on pourra appliquer des méthodes.

13-3-1-a. Utilisation des raw strings

La syntaxe des motifs comprend souvent le caractère contre-oblique qui doit être lui-même échappé dans une chaîne de caractères, ce qui alourdit la notation. On peut éviter cet inconvénient en utilisant des « chaînes brutes ». Par exemple au lieu de :

 
Sélectionnez
"\\d\\d ? \\w+ \\d{4}"

on écrira :

 
Sélectionnez
r"\d\d ? \w+ \d{4}"

13-3-1-b. Les options de compilation

Grâce à un jeu d'options de compilation, il est possible de piloter le comportement des expressions régulières. On utilise pour cela la syntaxe (?…) avec les drapeaux suivants :

  • a : correspondance ASCII (Unicode par défaut) ;
  • i : correspondance non sensible à la casse ;
  • L : les correspondances utilisent la locale, c'est-à-dire les particularités du pays ;
  • m : correspondance dans des chaînes multilignes ;
  • s : modifie le comportement du métacaractère point qui représentera alors aussi le saut de ligne ;
  • u : correspondance Unicode (par défaut) ;
  • x : mode verbeux.

Voici un exemple de recherche non sensible à la casse :

 
Sélectionnez
>>> import re
>>> case = re.compile(r"[a-z]+")
>>> ignore_case = re.compile(r"(?i)[a-z]+")
>>>
>>> print(case.search("Bastille").group())
astille
>>>
>>> print(ignore_case.search("Bastille").group())
Bastille

13-3-1-c. Les motifs nominatifs

Python possède une syntaxe qui permet de nommer des parties de motif délimitées par des parenthèses, ce qu'on appelle un motif nominatif :

  • syntaxe de création d'un motif nominatif : (?P<nom_du_motif>) ;
  • syntaxe permettant de s'y référer : (?P=nom_du_motif) ;
  • syntaxe à utiliser dans un motif de remplacement ou de substitution : \g<nom_du_motif>.

13-3-2. Exemples

On propose plusieurs exemples d'extraction de dates historiques telles que "14 juillet 1789".

13-3-2-a. Extraction simple

Cette chaîne peut être décrite par le motif \d\d? \w+ \d{4} que l'on peut expliciter ainsi : « un ou deux entiers décimaux suivi d'un blanc suivi d'une chaîne d'au moins un caractère suivi d'un blanc suivi de quatre entiers décimaux ».

Détaillons le script :

 
Sélectionnez
import re

motif_date = re.compile(r"\d\d ? \w+ \d{4}")
corresp = motif_date.search("Bastille le 14 juillet 1789")

print(corresp.group())

Après avoir importé le module re, la variable motif_date reçoit la forme compilée de l'expression régulière correspondant à une date historique. Puis on applique à ce motif compilé la méthode search() qui retourne la première position du motif dans la chaîne et l'affecte à la variable corresp. Enfin on affiche la correspondance complète (en ne donnant pas d'argument à group()).

Son exécution produit :

 
Sélectionnez
14 juillet 1789

13-3-2-b. Extraction des sous-groupes

On aurait pu affiner l'affichage du résultat en modifiant l'expression régulière de recherche de façon à pouvoir capturer les éléments du motif :

 
Sélectionnez
import re

motif_date = re.compile(r"(\d\d ?) (\w+) (\d{4})")
corresp = motif_date.search("Bastille le 14 juillet 1789")

print("corresp.group() :", corresp.group())
print("corresp.group(1) :", corresp.group(1))
print("corresp.group(2) :", corresp.group(2))
print("corresp.group(3) :", corresp.group(3))
print("corresp.group(1,3) :", corresp.group(1,3))
print("corresp.groups() :", corresp.groups())

Ce qui produit à l'exécution :

 
Sélectionnez
corresp.group() : 14 juillet 1789
corresp.group(1) : 14
corresp.group(2) : juillet
corresp.group(3) : 1789
corresp.group(1,3) : ('14', '1789')
corresp.groups() : ('14', 'juillet', '1789')

13-3-2-c. Extraction des sous-groupes nommés

Une autre possibilité est l'emploi de la méthode groupdict() qui renvoie une liste comportant le nom et la valeur des sous-groupes trouvés (ce qui nécessite de nommer les sous-groupes).

 
Sélectionnez
import re

motif_date = re.compile(r"(?P<jour>\d\d ?) (?P<mois>\w+) (\d{4})")
corresp = motif_date.search("Bastille le 14 juillet 1789")

print(corresp.groupdict())
print(corresp.group('jour'))
print(corresp.group('mois'))

Ce qui donne :

 
Sélectionnez
{'jour': '14', 'mois': 'juillet'}
14
juillet

13-3-2-d. Extraction d'une liste de sous-groupes

La méthode findall retourne une liste des occurrences trouvées. Si par exemple on désire extraire tous les nombres d'une chaîne, on peut écrire :

 
Sélectionnez
>>> import re
>>> nbr = re.compile(r"\d+")
>>> print(nbr.findall("Bastille le 14 juillet 1789"))
['14', '1789']

13-3-2-e. Scinder une chaîne

La méthode split() permet de scinder une chaîne à chaque occurrence du motif. Si on ajoute un paramètre numérique n (non nul), la chaîne est scindée en au plus n éléments :

 
Sélectionnez
>>> import re
>>> nbr = re.compile(r"\d+")
>>>
>>> print("Une coupure à chaque occurrence :", nbr.split("Bastille le 14 juillet 1789"))
Une coupure à chaque occurrence : ['Bastille le ', ' juillet ', '']
>>> print("Une seule coupure :", nbr.split("Bastille le 14 juillet 1789", 1))
Une seule coupure : ['Bastille le ', ' juillet 1789']

13-3-2-f. Substitution au sein d'une chaîne

La méthode sub() effectue des substitutions dans une chaîne. Le remplacement peut être une chaîne ou une fonction. Comme on le sait, en Python, les chaînes de caractères sont non modifiables et donc les substitutions produisent de nouvelles chaînes.

Exemples de remplacement d'une chaîne par une autre et des valeurs décimales en leur équivalent hexadécimal :

 
Sélectionnez
import re

def int2hexa(match) :
    return hex(int(match.group()))

anniv = re.compile(r"1789")
print("Premier anniversaire :", anniv.sub("1790", "Bastille le 14 juillet 1789"))

nbr = re.compile(r"\d+")
print("En hexa :", nbr.sub(int2hexa, "Bastille le 14 juillet 1789"))

Ce qui affiche :

 
Sélectionnez
Premier anniversaire : Bastille le 14 juillet
En hexa : Bastille le 0xe juillet 0x6fd

Les deux notations suivantes sont disponibles pour les substitutions :

  • & : contient toute la chaîne recherchée par un motif ;
  • \n : contient la sous-expression capturée par la ne paire de parenthèses du motif de recherche (kitxmlcodeinlinelatexdvp1 \le n \le 9finkitxmlcodeinlinelatexdvp).

précédentsommairesuivant
Ici, l'adjectif régulier est employé au sens de qui obéit à des règles.
Appelé aussi joker (ou wildcard en anglais).
En particulier le mathématicien Stephen Kleene (1909-1994).
Souvent abrégée en regex.
En anglais search pattern.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2015 Kordeo. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.