Les regex en Python
1. Raw string
Les string Python interprêtent certaines séquences (comme \n, \r, \t, \\, etc.)
de façon particulière.
Donc par exemple, pour représenter littéralement la chaîne "tangea\n\te" :
s1 = "tangea\n\te"
n’est pas la solution, puisque print(s1) donne :
tangea
e
Il faut écrire :
s1 = "tangea\\n\\te"
raw string est un type chaîne de caractères dans lequel il n’y a aucune interprétation :
s2 = r"tangea\n\te" (1)
print(s2)
print(s1==s2) # True
| 1 | le r qui signifie rawstring |
donne :
tangeante True
1.1. Raw string et regex
Dans une regex, le "\" a aussi une signification particulière.
Mémoriser une regex dans une string nécessite de prendre en compte ce double niveau d’interprétation.
Windows utilise le caractère "\" comme séparateur dans les noms de fichiers.
Par exemple, le homedir d’un utilisateur toto ressemble à
c:\users\toto\documents\.
Pour capturer l’utilisateur, on a envie d’écrire une Perl regex comme :
^c:\users\(.*?)\documents\$
mais comme dans une regex, le "\" possède une signification particulière,
la rexp correcte est :
^c:\\users\\(.*?)\\document\\$
Mémoriser cette regex dans une string Python donne donc :
s = "^c:\\\\users\\\\(.*?)\\\\document\\\\$"
Pas très limpide ! Utiliser une raw string rend les choses un peu plus lisibles :
s = r"^c:\\users\\(.*?)\\document\\$"
2. Le module re
Le module re offre des fonctions de manipulation de regex, principalement :
match(), search(), findall(), sub() et split().
Les regex connues de Python sont un sous-ensemble des Perl regex (autrement dit, Python gère toutes les regex étendues, plus certaines Perl regex).
Les regex pouvant apparaître abscons pour un néophyte,
lorsque les opérations à réaliser sont simples,
les méthodes de str (comme replace, translate, index, find, split, count) peuvent s’avérer suffisantes.
|
2.1. Rechercher une correspondance
match() détermine si une chaine commence par un motif donné, et
search() détermine s’il est présent.
la fonction match() est donc particulièrement mal nommée, puisqu’elle ne réalise pas ce qu’il est communément mis derrière matching a regex
|
Elles s’utilisent soit comme des fonctions de module :
import re
s = "Le navire roulait sous un ciel sans nuages"
print(re.match(r" un ",s)) # None
print(bool(re.match(r" un ",s))) # False
print(re.search(r" un ",s)) # <_sre.SRE_Match object at 0x7f752c57d648>
print(bool(re.search(r" un ",s))) # True
soit comme des méthodes de l’objet créé par re.compile() :
motif = re.compile(r" un ")
print(type(motif)) # <class '_sre.SRE_Pattern'>
print(bool(motif.match(s))) # False
print(bool(motif.search(s))) # True
En cas de succès, match() et search() renvoient un objet contenant la partie qui s’identifie au motif :
motif = re.compile(r"c.*l")
found=motif.match(s)
print(found) # None
found=motif.search(s)
print(found) # <_sre.SRE_Match object; span=(26, 30), match='ciel'>
Cet objet dispose des méthodes :
-
group()qui renvoie la portion qui s’identifie au motif, -
span()qui renvoie un tuple (position de début, position de fin)
print(found.group()) # ciel
print(found.span()) # (26, 30)
2.2. Rechercher toutes les occurrences d’un motif
findall() renvoie une liste contenant toutes les sous-chaines s’identifiant à un motif.
La version itérateur équivalente, finditer(), renvoie un itérateur parcourant toutes les sous-chaines s’identifiant à un motif
s = "Le navire roulait sous un ciel sans nuages"
found = re.findall(r"l.",s)
print(type(found)) # <class 'list'>
print(found) # ['la', 'l ']
2.3. Compiler une regex
motif = re.compile(r" un ") # compilation
found = motif.match(s) # confrontation
est donc globalement équivalent à :
found = re.match(r" un ",s) # compilation + confrontation
mais il est plus performat d’écrire :
motif = re.compile(r"^(.)(.).\2\1$")
for line in liste:
if motif.match(line):
print(line)
qui compile une fois pour toute la regex, que d’écrire :
for line in liste:
if re.match(r"^(.)(.).\2\1$",line):
print(line)
qui compile la regex à chaque tour de boucle.
Autre avantage, des options peuvent être spécifiées à compile() pour modifier le comportement des confrontations ultérieures.
Par exemple :
motif = re.compile(r"l.?e",re.IGNORECASE)
found=motif.findall(s)
print(found) # ['Le', 'la', 'l ']
2.4. Capturer
On capture avec des () dans la regex et on récupère la capture avec les méthodes group() et groups().
Exemple :
import re
s = "Le navire roulait sous un ciel sans nuages"
capt = re.search(r" (.*?) .*n (.*?) ",s)
print(bool(capt)) # True
print(capt) # <re.Match object; span=(2, 31), match=' navire roulait sous un ciel '>
print(capt.groups()) # ('navire', 'ciel')
print(capt.group(1)) # navire
print(capt.group(2)) # ciel
2.5. Substituer
C’est le rôle de la méthode sub.
La regex identifie la portion à remplacer, et sub spécifie par quoi il faut la remplacer.
Exemple :
import re
s = "Le navire roulait sous un ciel sans nuages"
capt = re.sub(r"\b(..)\b","[mot de 2 lettres]",s)
print(capt) # [mot de 2 lettres] navire roulait sous [mot de 2 lettres] ciel sans nuages
La regex peut capturer des portions, qui peuvent être réutilisées dans la substitution avec \1, \2, etc. Exemple :
motif = re.compile(r'^\s*(.*)\s*$')
line = " 12 300 "
print(motif.sub(r'\1',line)) # -> "12 300"