Guide des expressions régulières¶
Author: | A.M. Kuchling <amk@amk.ca> |
---|
Résumé
Ce document constitue un guide d’introduction à l’utilisation des expressions régulières en Python avec le module re
. Il fournit une introduction plus abordable que la section correspondante dans le guide de référence de la bibliothèque.
Introduction¶
Les expressions régulières (notées RE ou motifs regex dans ce document) sont essentiellement un petit langage de programmation hautement spécialisé embarqué dans Python et dont la manipulation est rendue possible par l’utilisation du module re
. En utilisant ce petit langage, vous définissez des règles pour spécifier une correspondance avec un ensemble souhaité de chaînes de caractères ; ces chaînes peuvent être des phrases, des adresses de courriel, des commandes TeX ou tout ce que vous voulez. Vous pouvez ensuite poser des questions telles que « Est-ce que cette chaîne de caractères correspond au motif ? » ou « Y a-t-il une correspondance pour ce motif à l’intérieur de la chaîne de caractères ? ». Vous pouvez aussi utiliser les RE pour modifier une chaîne de caractères ou la découper de différentes façons.
Un motif d’expression régulière est compilé en code intermédiaire (bytecode en anglais) qui est ensuite exécuté par un moteur de correspondance écrit en C. Pour une utilisation plus poussée, il peut s’avérer nécessaire de s’intéresser à la manière dont le moteur exécute la RE afin d’écrire une expression dont le code intermédiaire est plus rapide. L’optimisation n’est pas traitée dans ce document, parce qu’elle nécessite d’avoir une bonne compréhension des mécanismes internes du moteur de correspondance.
Le langage des expressions régulières est relativement petit et restreint, donc toutes les tâches de manipulation de chaînes de caractères ne peuvent pas être réalisées à l’aide d’expressions régulières. Il existe aussi des tâches qui peuvent être réalisées à l’aide d’expressions régulières mais qui ont tendance à produire des expressions régulières très compliquées. Dans ces cas, il est plus utile d’écrire du code Python pour réaliser le traitement ; même si le code Python est plus lent qu’une expression régulière élaborée, il sera probablement plus compréhensible.
Motifs simples¶
Nous commençons par étudier les expressions régulières les plus simples. Dans la mesure où les expressions régulières sont utilisées pour manipuler des chaînes de caractères, nous commençons par l’action la plus courante : la correspondance de caractères.
Pour une explication détaillée sur le concept informatique sous-jacent aux expressions régulières (automate à états déterministe ou non-déterministe), vous pouvez vous référer à n’importe quel manuel sur l’écriture de compilateurs.
Correspondance de caractères¶
La plupart des lettres ou caractères correspondent simplement à eux-mêmes. Par exemple, l’expression régulière test
correspond à la chaîne de caractères test
, précisément. Vous pouvez activer le mode non-sensible à la casse qui permet à cette RE de correspondre également à Test
ou TEST
(ce sujet est traité par la suite).
Il existe des exceptions à cette règle ; certains caractères sont des métacaractères spéciaux et ne correspondent pas à eux-mêmes. Au lieu de cela, ils signalent que certaines choses non ordinaires doivent correspondre, ou ils affectent d’autre portions de la RE en les répétant ou en changeant leur sens. Une grande partie de ce document est consacrée au fonctionnement de ces métacaractères.
Voici une liste complète des métacaractères ; leur sens est décrit dans la suite de ce guide.
. ^ $ * + ? { } [ ] \ | ( )
Les premiers métacaractères que nous étudions sont [
et ]
. Ils sont utilisés pour spécifier une classe de caractères, qui forme un ensemble de caractères dont vous souhaitez trouver la correspondance. Les caractères peuvent être listés individuellement, ou une plage de caractères peut être indiquée en fournissant deux caractères séparés par un “-“`. Par exemple, [abc]
correspond à n’importe quel caractère parmi a
, b
, ou c
; c’est équivalent à [a-c]
, qui utilise une plage pour exprimer le même ensemble de caractères. Si vous voulez trouver une chaîne qui ne contient que des lettres en minuscules, la RE est [a-z]
.
Les métacaractères ne sont pas actifs dans les classes. Par exemple, [akm$]
correspond à n’importe quel caractère parmi 'a'
, 'k'
, 'm'
ou '$'
; '$'
est habituellement un métacaractère mais dans une classe de caractères, il est dépourvu de sa signification spéciale.
Vous pouvez trouver une correspondance avec les caractères non listés dans une classe en spécifiant le complément de l’ensemble. Ceci est indiqué en plaçant un '^'
en tant que premier caractère de la classe ; '^'
en dehors d’une classe de caractères correspond au caractère '^'
. Par exemple, [^5]
correspond à tous les caractères, sauf '5'
.
Le métacaractère le plus important est probablement la barre oblique inverse ( backslash en anglais), \
. Comme dans les chaînes de caractères en Python, la barre oblique inverse peut être suivie par différents caractères pour signaler différentes séquences spéciales. Elle est aussi utilisée pour échapper tous les métacaractères afin d’en trouver les correspondances dans les motifs ; par exemple, si vous devez trouver une correspondance pour [
ou \
, vous pouvez les précéder avec une barre oblique inverse pour annuler leur signification spéciale : \[
ou \\
.
Certaines séquences spéciales commençant par '\'
représentent des ensembles de caractères prédéfinis qui sont souvent utiles, tels que l’ensemble des chiffres, l’ensemble des lettres ou l’ensemble des caractères qui ne sont pas des « blancs ».
Prenons un exemple : \w
correspond à n’importe quel caractère alphanumérique. Si l’expression régulière est exprimée en bytes, c’est équivalent à la classe [a-zA-Z0-9_]
. Si l’expression régulière est une chaîne de caractères, \w
correspond à tous les caractères identifiés comme lettre dans la base de données Unicode fournie par le module unicodedata
. Vous pouvez utiliser la définition plus restrictive de \w
dans un motif exprimé en chaîne de caractères en spécifiant l’option re.ASCII
lors de la compilation de l’expression régulière.
La liste de séquences spéciales suivante est incomplète. Pour une liste complète des séquences et définitions de classes étendues relatives aux motifs de chaînes de caractères Unicode, reportez-vous à la dernière partie de la référence Syntaxe d’Expressions Régulières de la bibliothèque standard. En général, les versions Unicode trouvent une correspondance avec n’importe quel caractère présent dans la catégorie appropriée de la base de données Unicode.
\d
- Correspond à n’importe quel caractère numérique ; équivalent à la classe
[0-9]
. \D
- Correspond à n’importe caractère non numérique ; équivalent à la classe
[^0-9]
. \s
- Correspond à n’importe quel caractère « blanc » ; équivalent à la classe
[ \t\n\r\f\v]
. \S
- Correspond à n’importe caractère autre que « blanc » ; équivalent à la classe
[^ \t\n\r\f\v]
. \w
- Correspond à n’importe caractère alphanumérique ; équivalent à la classe
[a-zA-Z0-9_]
. \W
- Correspond à n’importe caractère non-alphanumérique ; équivalent à la classe
[^a-zA-Z0-9_]
.
Ces séquences peuvent être incluses dans une classe de caractères. Par exemple, [\s,.]
est une classe de caractères qui correspond à tous les caractères « blancs » ou ','
ou '.'
.
The final metacharacter in this section is .
. It matches anything except a
newline character, and there’s an alternate mode (re.DOTALL
) where it will
match even a newline. '.'
is often used where you want to match « any
character ».
Répétitions¶
Trouver des correspondances de divers ensembles de caractères est la première utilisation des expressions régulières, ce que l’on ne peut pas faire avec les méthodes des chaînes. Cependant, si c’était la seule possibilité des expressions régulières, le gain ne serait pas significatif. Une autre utilisation consiste à spécifier des portions d’une RE qui peuvent être répétées un certain nombre de fois.
The first metacharacter for repeating things that we’ll look at is *
. *
doesn’t match the literal character *
; instead, it specifies that the
previous character can be matched zero or more times, instead of exactly once.
For example, ca*t
will match ct
(0 a
characters), cat
(1 a
),
caaat
(3 a
characters), and so forth. The RE engine has various
internal limitations stemming from the size of C’s int
type that will
prevent it from matching over 2 billion a
characters; patterns
are usually not written to match that much data.
Les répétitions telles que *
sont gloutonnes ; quand vous répétez une RE, le moteur de correspondance essaie de trouver la correspondance la plus longue en répétant le motif tant qu’il le peut. Si la suite du motif ne correspond pas, le moteur de correspondance revient en arrière et essaie avec moins de répétitions.
A step-by-step example will make this more obvious. Let’s consider the
expression a[bcd]*b
. This matches the letter 'a'
, zero or more letters
from the class [bcd]
, and finally ends with a 'b'
. Now imagine matching
this RE against the string abcbd
.
Étape | Correspond | Explication |
---|---|---|
1 | a |
Le a correspond dans la RE. |
2 | abcbd |
Le moteur de correspondance trouve [bcd]* , va aussi loin qu’il le peut, ce qui n’est pas la fin de la chaîne. |
3 | échec | Le moteur essaie de trouver une correspondance avec b mais la position courante est à la fin de la chaîne de caractères, donc il échoue. |
4 | abcb |
Retour en arrière, de manière à ce que [bcd]* corresponde avec un caractère de moins. |
5 | échec | Essaie encore b , mais la position courante est le dernier caractère, qui est 'd' . |
6 | abc |
Encore un retour en arrière, de manière à ce que [bcd]* ne corresponde qu’à bc . |
6 | abcb |
Essaie b encore une fois. Cette fois, le caractère à la position courante est 'b' , donc cela fonctionne. |
The end of the RE has now been reached, and it has matched abcb
. This
demonstrates how the matching engine goes as far as it can at first, and if no
match is found it will then progressively back up and retry the rest of the RE
again and again. It will back up until it has tried zero matches for
[bcd]*
, and if that subsequently fails, the engine will conclude that the
string doesn’t match the RE at all.
Another repeating metacharacter is +
, which matches one or more times. Pay
careful attention to the difference between *
and +
; *
matches
zero or more times, so whatever’s being repeated may not be present at all,
while +
requires at least one occurrence. To use a similar example,
ca+t
will match cat
(1 a
), caaat
(3 a
’s), but won’t match
ct
.
There are two more repeating qualifiers. The question mark character, ?
,
matches either once or zero times; you can think of it as marking something as
being optional. For example, home-?brew
matches either homebrew
or
home-brew
.
The most complicated repeated qualifier is {m,n}
, where m and n are
decimal integers. This qualifier means there must be at least m repetitions,
and at most n. For example, a/{1,3}b
will match a/b
, a//b
, and
a///b
. It won’t match ab
, which has no slashes, or a////b
, which
has four.
You can omit either m or n; in that case, a reasonable value is assumed for the missing value. Omitting m is interpreted as a lower limit of 0, while omitting n results in an upper bound of infinity — actually, the upper bound is the 2-billion limit mentioned earlier, but that might as well be infinity.
Le lecteur attentif aura noté que les trois premiers quantificateurs peuvent être exprimés en utilisant cette notation. {0,}
est la même chose que *
, {1,}
est équivalent à +
et {0,1}
se comporte comme ?
. Il est préférable d’utiliser *
, +
ou ?
quand vous le pouvez, simplement parce qu’ils sont plus courts et plus faciles à lire.
Utilisation des expressions régulières¶
Maintenant que nous avons vu quelques expressions régulières simples, utilisons-les concrètement. Le module re
fournit une interface pour le moteur de correspondance, ce qui permet de compiler les RE en objets et d’effectuer des correspondances avec.
Compilation des expressions régulières¶
Les expressions régulières sont compilées en objets motifs, qui possèdent des méthodes pour diverses opérations telles que la recherche de correspondances ou les substitutions dans les chaînes.
>>> import re
>>> p = re.compile('ab*')
>>> p
re.compile('ab*')
re.compile()
accepte aussi une option flags, utilisée pour activer des fonctionnalités particulières et des variations de syntaxe. Nous étudierons la définition des variables plus tard et, pour l’instant, un seul exemple suffit :
>>> p = re.compile('ab*', re.IGNORECASE)
La RE passée à re.compile()
est une chaîne. Les RE sont des chaînes car les expressions régulières ne font pas partie intrinsèque du langage Python et aucune syntaxe particulière n’a été créée pour les exprimer (il existe des applications qui ne nécessitent aucune RE et il n’a donc aucune raison de grossir les spécifications du langage en incluant les RE). Ainsi, le module re
est simplement un module d’extension en C inclus dans Python, tout comme les modules socket
ou zlib
.
Exprimer les RE comme des chaînes de caractères garde le langage Python plus simple mais introduit un inconvénient qui fait l’objet de la section suivante.
La maudite barre oblique inverse¶
Comme indiqué précédemment, les expressions régulières utilisent la barre oblique inverse (backslash en anglais) pour indiquer des constructions particulières ou pour autoriser des caractères spéciaux sans que leur signification spéciale ne soit invoquée. C’est en contradiction avec l’usage de Python qui est qu’un caractère doit avoir la même signification dans les littéraux de chaînes de caractères.
Considérons que vous voulez écrire une RE qui fait correspondre la chaîne de caractères \section
(on en trouve dans un fichier LaTeX). Pour savoir ce qu’il faut coder dans votre programme, commençons par la chaîne de caractères cherchée. Ensuite, nous devons échapper chaque barre oblique inverse et tout autre métacaractère en les précédant d’une barre oblique inverse, ce qui donne la chaîne de caractères \\section
. La chaîne résultante qui doit être passée à re.compile()
est donc \\section
. Comme nous devons l’exprimer sous la forme d’une chaîne littérale Python, nous devons échapper les deux barres obliques inverses encore une fois.
Caractères | Niveau |
---|---|
\section |
Chaîne de caractère à chercher |
\\section |
Barre oblique inverse échappée pour re.compile() |
"\\\\section" |
Barres obliques inverses échappées pour un littéral de chaîne de caractères |
Pour faire court, si vous cherchez une correspondance pour une barre oblique inverse littérale, écrivez '\\\\'
dans votre chaîne RE, car l’expression régulière doit être \\
et que chaque barre oblique inverse doit être exprimée comme \\
dans un littéral chaîne de caractères Python. Dans les RE qui comportent plusieurs barres obliques inverses, cela conduit à beaucoup de barres obliques inverses et rend la chaîne résultante difficile à comprendre.
La solution consiste à utiliser les chaînes brutes Python pour les expressions régulières ; les barres obliques inverses ne sont pas gérées d’une manière particulière dans les chaînes littérales préfixées avec 'r'
. Ainsi, r"\n"
est la chaîne de deux caractères contenant '\'
et 'n'
alors que "\n"
est la chaîne contenant uniquement le caractère retour à la ligne. Les expressions régulières sont souvent écrites dans le code Python en utilisant la notation « chaînes brutes ».
Chaîne normale | Chaîne de caractères brute |
---|---|
"ab*" |
r"ab*" |
"\\\\section" |
r"\\section" |
"\\w+\\s+\\1" |
r"\w+\s+\1" |
Recherche de correspondances¶
Une fois que nous avons un objet représentant une expression régulière compilée, qu’en faisons-nous ? Les objets motifs ont plusieurs méthodes et attributs. Seuls les plus significatifs seront couverts ici ; consultez la documentation re
pour la liste complète.
Méthode/Attribut | Objectif |
---|---|
match() |
Détermine si la RE fait correspond dès le début de la chaîne. |
search() |
Analyse la chaîne à la recherche d’une position où la RE correspond. |
findall() |
Trouve toutes les sous-chaînes qui correspondent à la RE et les renvoie sous la forme d’une liste. |
finditer() |
Trouve toutes les sous-chaînes qui correspondent à la RE et les renvoie sous la forme d’un itérateur. |
match()
and search()
return None
if no match can be found. If
they’re successful, a match object instance is returned,
containing information about the match: where it starts and ends, the substring
it matched, and more.
Vous pouvez apprendre leur fonctionnement en expérimentant de manière interactive avec le module re
. Si vous disposez de tkinter
, vous pouvez aussi regarder les sources de Tools/demo/redemo.py, un programme de démonstration inclus dans la distribution Python. Ce programme vous permet de rentrer des RE et des chaînes, affichant si la RE correspond ou pas. redemo.py
peut s’avérer particulièrement utile quand vous devez déboguer une RE compliquée.
Ce guide utilise l’interpréteur standard de Python pour ses exemples. Commencez par lancer l’interpréteur Python, importez le module re
et compilez une RE :
>>> import re
>>> p = re.compile('[a-z]+')
>>> p
re.compile('[a-z]+')
Now, you can try matching various strings against the RE [a-z]+
. An empty
string shouldn’t match at all, since +
means “one or more repetitions”.
match()
should return None
in this case, which will cause the
interpreter to print no output. You can explicitly print the result of
match()
to make this clear.
>>> p.match("")
>>> print(p.match(""))
None
Now, let’s try it on a string that it should match, such as tempo
. In this
case, match()
will return a match object, so you
should store the result in a variable for later use.
>>> m = p.match('tempo')
>>> m
<_sre.SRE_Match object; span=(0, 5), match='tempo'>
Now you can query the match object for information about the matching string. match object instances also have several methods and attributes; the most important ones are:
Méthode/Attribut | Objectif |
---|---|
group() |
Renvoie la chaîne de caractères correspondant à la RE |
start() |
Renvoie la position de début de la correspondance |
end() |
Renvoie la position de fin de la correspondance |
span() |
Renvoie un tuple contenant les positions (début, fin) de la correspondance |
Essayons ces méthodes pour clarifier leur signification :
>>> m.group()
'tempo'
>>> m.start(), m.end()
(0, 5)
>>> m.span()
(0, 5)
group()
returns the substring that was matched by the RE. start()
and end()
return the starting and ending index of the match. span()
returns both start and end indexes in a single tuple. Since the match()
method only checks if the RE matches at the start of a string, start()
will always be zero. However, the search()
method of patterns
scans through the string, so the match may not start at zero in that
case.
>>> print(p.match('::: message'))
None
>>> m = p.search('::: message'); print(m)
<_sre.SRE_Match object; span=(4, 11), match='message'>
>>> m.group()
'message'
>>> m.span()
(4, 11)
Dans les programmes réels, la façon de faire la plus courante consiste à stocker l’objet correspondance dans une variable, puis à vérifier s’il vaut None
. Généralement, cela ressemble à ceci :
p = re.compile( ... )
m = p.match( 'string goes here' )
if m:
print('Match found: ', m.group())
else:
print('No match')
Two pattern methods return all of the matches for a pattern.
findall()
returns a list of matching strings:
>>> p = re.compile('\d+')
>>> p.findall('12 drummers drumming, 11 pipers piping, 10 lords a-leaping')
['12', '11', '10']
findall()
has to create the entire list before it can be returned as the
result. The finditer()
method returns a sequence of
match object instances as an iterator:
>>> iterator = p.finditer('12 drummers drumming, 11 ... 10 ...')
>>> iterator
<callable_iterator object at 0x...>
>>> for match in iterator:
... print(match.span())
...
(0, 2)
(22, 24)
(29, 31)
Fonctions de niveau module¶
Vous n’avez pas besoin de créer un objet motif et d’appeler ses méthodes ; le module re
fournit des fonctions à son niveau, ce sont match()
, search()
, findall()
, sub()
et ainsi de suite. Ces fonctions prennent les mêmes arguments que les méthodes correspondantes des objets motifs, avec la chaîne RE ajoutée en tant que premier argument. Elles renvoient toujours None
ou une instance d’objet correspondance.
>>> print(re.match(r'From\s+', 'Fromage amk'))
None
>>> re.match(r'From\s+', 'From amk Thu May 14 19:12:10 1998')
<_sre.SRE_Match object; span=(0, 5), match='From '>
En interne, ces fonctions créent simplement un objet motif pour vous et appellent la méthode appropriée de cet objet. Elles stockent également l’objet compilé dans un cache afin que les appels suivants qui utilisent la même RE n’aient pas besoin d’analyser le motif une nouvelle fois.
Devez-vous utiliser ces fonctions au niveau des modules ou devez-vous calculer le motif et appeler vous-même ses méthodes ? Si vous utilisez l’expression régulière à l’intérieur d’une boucle, la pré-compilation permet d’économiser quelques appels de fonctions. En dehors des boucles, il n’y a pas beaucoup de différence grâce au cache interne.
Options de compilation¶
Les options de compilation vous permettent de modifier le comportement des expressions régulières. Ces options sont accessibles dans le module re
par deux noms, un long du type IGNORECASE
et un court (une seule lettre) tel que I
(si vous êtes habitués aux modificateurs de motifs Perl, la version courte utilise les mêmes lettres que Perl, par exemple la version courte de re.VERBOSE
est re.X
). Plusieurs options peuvent être spécifiées en appliquant l’opérateur bit-à-bit OR ; par exemple, re.I | re.M
active à la fois les options I
et M
.
Vous trouvez ci-dessous le tableau des options disponibles, suivies d’explications détaillées.
Option | Signification |
---|---|
ASCII , A |
Transforme plusieurs échappements tels que \w , \b , \s et \d de manière à ce qu’ils ne correspondent qu’à des caractères ASCII ayant la propriété demandée. |
DOTALL , S |
Make . match any character, including
newlines |
IGNORECASE , I |
Do case-insensitive matches |
LOCALE , L |
Do a locale-aware match |
MULTILINE , M |
Multi-line matching, affecting ^ and
$ |
VERBOSE , X (pour extended, c-à-d étendu en anglais) |
Active les RE verbeuses, qui peuvent être organisées de manière plus propre et compréhensible. |
-
I
-
IGNORECASE
Perform case-insensitive matching; character class and literal strings will match letters by ignoring case. For example,
[A-Z]
will match lowercase letters, too, andSpam
will matchSpam
,spam
, orspAM
. This lowercasing doesn’t take the current locale into account; it will if you also set theLOCALE
flag.
-
L
-
LOCALE
Make
\w
,\W
,\b
, and\B
, dependent on the current locale instead of the Unicode database.Locales are a feature of the C library intended to help in writing programs that take account of language differences. For example, if you’re processing French text, you’d want to be able to write
\w+
to match words, but\w
only matches the character class[A-Za-z]
; it won’t match'é'
or'ç'
. If your system is configured properly and a French locale is selected, certain C functions will tell the program that'é'
should also be considered a letter. Setting theLOCALE
flag when compiling a regular expression will cause the resulting compiled object to use these C functions for\w
; this is slower, but also enables\w+
to match French words as you’d expect.
-
M
-
MULTILINE
Nota :
^
et$
n’ont pas encore été expliqués ; ils sont introduits dans la section Plus de métacaractères.Normalement,
^
correspond uniquement au début de la chaîne, et$
correspond uniquement à la fin de la chaîne et immédiatement avant la nouvelle ligne (s’il y en a une) à la fin de la chaîne. Lorsque cette option est spécifiée,^
correspond au début de la chaîne de caractères et au début de chaque ligne de la chaîne de caractères, immédiatement après le début de la nouvelle ligne. De même, le métacaractère$
correspond à la fin de la chaîne de caractères ou à la fin de chaque ligne (précédant immédiatement chaque nouvelle ligne).
-
S
-
DOTALL
Fait que le caractère spécial
'.'
corresponde avec n’importe quel caractère, y compris le retour à la ligne ; sans cette option,'.'
correspond avec tout, sauf le retour à la ligne.
-
A
-
ASCII
Fait que
\w
,\W
,\b
,\B
,\s
et\S
ne correspondent qu’avec des caractères ASCII au lieu de l’ensemble des caractères Unicode. Cette option n’a de sens que pour des motifs Unicode, elle est ignorée pour les motifs bytes.
-
X
-
VERBOSE
Cette option vous permet d’écrire des expressions régulières plus lisibles en vous permettant plus de flexibilité pour le formatage. Lorsque cette option est activée, les « blancs » dans la chaîne RE sont ignorés, sauf lorsque le « blanc » se trouve dans une classe de caractères ou est précédé d’une barre oblique inverse ; ceci vous permet d’organiser et d’indenter vos RE plus clairement. Cette option vous permet également de placer des commentaires dans une RE, ils seront ignorés par le moteur ; les commentaires commencent par un
'#'
qui n’est ni dans une classe de caractères, ni précédé d’une barre oblique inverse.Par exemple, voici une RE qui utilise
re.VERBOSE
; vous pouvez constater qu’elle est beaucoup plus facile à lire :charref = re.compile(r""" &[#] # Start of a numeric entity reference ( 0[0-7]+ # Octal form | [0-9]+ # Decimal form | x[0-9a-fA-F]+ # Hexadecimal form ) ; # Trailing semicolon """, re.VERBOSE)
Sans l’option verbeuse, cette RE ressemble à ceci :
charref = re.compile("&#(0[0-7]+" "|[0-9]+" "|x[0-9a-fA-F]+);")
Dans l’exemple ci-dessus, Python concatène automatiquement les littéraux chaînes de caractères qui ont été utilisés pour séparer la RE en petits morceaux, mais la RE reste plus difficile à comprendre que sa version utilisant
re.VERBOSE
.
Des motifs plus puissants¶
Jusqu’à présent nous avons seulement couvert une partie des fonctionnalités des expressions régulières. Dans cette section, nous couvrirons quelques nouveaux métacaractères et l’utilisation des groupes pour récupérer des portions de textes correspondantes.
Plus de métacaractères¶
Nous n’avons pas encore couvert tous les métacaractères. Cette section traite de la plupart de ceux que nous n’avons pas abordés.
Certains métacaractères restants sont des assertions de largeur zéro (zero-width assertions en anglais). Ils ne font pas avancer le moteur dans la chaîne de caractères ; ils ne consomment aucun caractère et ils réussissent ou échouent tout simplement. Par exemple, \b
est une assertion selon laquelle la position actuelle est située à la limite d’un mot ; la position n’est pas modifiée par le » b ». Cela signifie que les assertions de largeur zéro ne doivent pas être répétées car, si elles correspondent à un endroit donné, elles correspondent automatiquement un nombre infini de fois.
|
Alternation, or the « or » operator. If A and B are regular expressions,
A|B
will match any string that matches eitherA
orB
.|
has very low precedence in order to make it work reasonably when you’re alternating multi-character strings.Crow|Servo
will match eitherCrow
orServo
, notCro
, a'w'
or an'S'
, andervo
.Pour correspondre avec un
'|'
littéral, utilisez\|
ou placez-le dans une classe de caractères, comme ceci[|]
.^
Correspond à un début de ligne. À moins que l’option
MULTILINE
ne soit activée, cela ne fait correspondre que le début de la chaîne. Dans le modeMULTILINE
, cela fait aussi correspondre immédiatement après chaque nouvelle ligne à l’intérieur de la chaîne.Par exemple, si vous voulez trouver le mot
From
uniquement quand il est en début de ligne, la RE à utiliser est^From
.>>> print(re.search('^From', 'From Here to Eternity')) <_sre.SRE_Match object; span=(0, 4), match='From'> >>> print(re.search('^From', 'Reciting From Memory')) None
$
Correspond à une fin de ligne, ce qui veut dire soit la fin de la chaîne, soit tout emplacement qui est suivi du caractère de nouvelle ligne.
>>> print(re.search('}$', '{block}')) <_sre.SRE_Match object; span=(6, 7), match='}'> >>> print(re.search('}$', '{block} ')) None >>> print(re.search('}$', '{block}\n')) <_sre.SRE_Match object; span=(6, 7), match='}'>
Pour trouver un
'$'
littéral, utilisez\$
ou placez-le à l’intérieur d’une classe de caractères, comme ceci[$]
.\A
- Correspond au début de la chaîne de caractère, uniquement. Si l’option
MULTILINE
n’est pas activée,\A
et^
sont équivalents. Dans le modeMULTILINE
, ils sont différents :\A
ne correspond toujours qu’au début de la chaîne alors que^
correspond aussi aux emplacements situés immédiatement après une nouvelle ligne à l’intérieur de la chaîne. \Z
- Correspond uniquement à la fin d’une chaîne de caractères.
\b
Limite de mot. C’est une assertion de largeur zéro qui correspond uniquement aux positions de début et de fin de mot. Un mot est défini comme une séquence de caractères alphanumériques ; ainsi, la fin d’un mot est indiquée par un « blanc » ou un caractère non-alphanumérique.
L’exemple suivant fait correspondre
class
seulement si c’est un mot complet ; il n’y a pas de correspondance quand il est à l’intérieur d’un autre mot.>>> p = re.compile(r'\bclass\b') >>> print(p.search('no class at all')) <_sre.SRE_Match object; span=(3, 8), match='class'> >>> print(p.search('the declassified algorithm')) None >>> print(p.search('one subclass is')) None
Quand vous utilisez cette séquence spéciale, gardez deux choses à l’esprit. Tout d’abord, c’est la pire collision entre les littéraux des chaînes Python et les séquences d’expressions régulières. Dans les littéraux de chaîne de caractères Python,
\b`
est le caractère de retour-arrière (backspace en anglais), dont la valeur ASCII est 8. Si vous n’utilisez pas les chaînes de caractères brutes, alors Python convertit le\b
en retour-arrière, et votre RE ne correspond pas à ce que vous attendez. L’exemple suivant ressemble à notre RE précédente, mais nous avons omis le`'r'
devant la chaîne RE.>>> p = re.compile('\bclass\b') >>> print(p.search('no class at all')) None >>> print(p.search('\b' + 'class' + '\b')) <_sre.SRE_Match object; span=(0, 7), match='\x08class\x08'>
Ensuite, dans une classe de caractères, où cette assertion n’a pas lieu d’être,
\b
représente le caractère retour-arrière, afin d’être compatible avec les littéraux de chaînes de caractères.\B
- Encore une assertion de largeur zéro, qui est l’opposée de
\b
, c’est-à-dire qu’elle fait correspondre uniquement les emplacements qui ne sont pas à la limite d’un mot.
Regroupement¶
Frequently you need to obtain more information than just whether the RE matched
or not. Regular expressions are often used to dissect strings by writing a RE
divided into several subgroups which match different components of interest.
For example, an RFC-822 header line is divided into a header name and a value,
separated by a ':'
, like this:
From: author@example.com
User-Agent: Thunderbird 1.5.0.9 (X11/20061227)
MIME-Version: 1.0
To: editor@example.com
Vous pouvez alors écrire une expression régulière qui fait correspondre une ligne d’en-tête entière et qui comporte un groupe correspondant au nom de l’en-tête, et un autre groupe correspondant à la valeur de l’en-tête.
Les groupes sont délimités par les métacaractères marqueurs '('``et ``')'
. '('
et ')'
ont à peu près le même sens que dans les expressions mathématiques ; ils forment un groupe à partir des expressions qu’ils encadrent ; vous pouvez répéter le contenu d’un groupe à l’aide d’un quantificateur, comme *
, +
, ?
ou {m,n}
. Par exemple, (ab)*
correspond à zéro, une ou plusieurs fois ab
.
>>> p = re.compile('(ab)*')
>>> print(p.match('ababababab').span())
(0, 10)
Groups indicated with '('
, ')'
also capture the starting and ending
index of the text that they match; this can be retrieved by passing an argument
to group()
, start()
, end()
, and span()
. Groups are
numbered starting with 0. Group 0 is always present; it’s the whole RE, so
match object methods all have group 0 as their default
argument. Later we’ll see how to express groups that don’t capture the span
of text that they match.
>>> p = re.compile('(a)b')
>>> m = p.match('ab')
>>> m.group()
'ab'
>>> m.group(0)
'ab'
Les sous-groupes sont numérotés de la gauche vers la droite, à partir de 1. Les groupes peuvent être imbriqués ; pour déterminer le numéro, il vous suffit de compter le nombre de parenthèses ouvrantes de la gauche vers la droite.
>>> p = re.compile('(a(b)c)d')
>>> m = p.match('abcd')
>>> m.group(0)
'abcd'
>>> m.group(1)
'abc'
>>> m.group(2)
'b'
group()
can be passed multiple group numbers at a time, in which case it
will return a tuple containing the corresponding values for those groups.
>>> m.group(2,1,2)
('b', 'abc', 'b')
The groups()
method returns a tuple containing the strings for all the
subgroups, from 1 up to however many there are.
>>> m.groups()
('abc', 'b')
Les renvois dans un motif vous permettent de spécifier que le contenu d’un groupe précédent doit aussi être trouvé à l’emplacement actuel dans la chaîne. Par exemple, \1
réussit si le contenu du premier groupe se trouve aussi à la position courante, sinon il échoue. Rappelez-vous que les littéraux de chaînes Python utilisent aussi la barre oblique inverse suivie d’un nombre pour insérer des caractères arbitraires dans une chaîne ; soyez sûr d’utiliser une chaîne brute quand vous faites des renvois dans une RE.
Par exemple, la RE suivante détecte les mots doublés dans une chaîne.
>>> p = re.compile(r'(\b\w+)\s+\1')
>>> p.search('Paris in the the spring').group()
'the the'
Les renvois tels que celui-ci ne sont pas très utiles pour effectuer une simple recherche dans une chaîne — il n’y a que peu de formats de textes qui répètent des données ainsi — mais vous verrez bientôt qu’ils sont très utiles pour effectuer des substitutions dans les chaînes.
Groupes non de capture et groupes nommés¶
Les RE élaborées peuvent utiliser de nombreux groupes, à la fois pour capturer des sous-chaînes intéressantes ainsi que pour regrouper et structurer la RE elle-même. Dans les RE complexes, il devient difficile de garder la trace des numéros de groupes. Deux caractéristiques aident à résoudre ce problème, toutes deux utilisant la même syntaxe d’extension des expressions régulières. Nous allons donc commencer en examinant cette syntaxe.
Les puissantes extensions des expressions régulières de Perl 5 sont réputées. Pour les mettre en œuvre, les développeurs Perl ne pouvaient pas utiliser de nouveaux métacaractères simples ou de nouvelles séquences commençant par \
sans que les RE Perl ne deviennent trop différentes des RE standards au point de créer de la confusion. S’ils avaient choisi &
comme nouveau métacaractère, par exemple, les expressions déjà écrites auraient considéré que &
était un caractère standard et ne l’aurait pas échappé en écrivant \&
ou [&]
.
La solution adoptée par les développeurs Perl a été d’utiliser (?...)
comme syntaxe d’extension. Placer ?
immédiatement après une parenthèse était une erreur de syntaxe, parce que le ?
n’a alors rien à répéter. Ainsi, cela n’a pas introduit de problème de compatibilité. Les caractères qui suivent immédiatement le ?
indiquent quelle extension est utilisée, donc (?=truc)
est une chose (une assertion positive anticipée) et (?:truc)
est une autre chose (la sous-expression truc
que l’on groupe).
Python gère plusieurs des extensions Perl et rajoute une extension à la syntaxe des extensions Perl. Si le premier caractère après le point d’interrogation est P
, cela signifie que c’est une extension spécifique à Python.
Après avoir vu la syntaxe générale d’extension des RE, nous pouvons revenir aux fonctionnalités qui simplifient le travail avec les groupes dans des RE complexes.
Parfois, vous souhaitez utiliser un groupe pour marquer une partie de l’expression régulière mais le contenu de ce groupe ne vous intéresse pas vraiment. Vous pouvez l’indiquer explicitement en utilisant la syntaxe de groupe, mais sans indiquer que vous voulez en capturer le contenu : (?:...)
, où vous remplacez les ...
par n’importe quelle expression régulière.
>>> m = re.match("([abc])+", "abc")
>>> m.groups()
('c',)
>>> m = re.match("(?:[abc])+", "abc")
>>> m.groups()
()
À part le fait que vous n’avez pas accès au contenu du groupe, un groupe se comporte exactement de la même manière qu’un groupe de capture ; vous pouvez placer n’importe quoi dedans, spécifier une répétition avec un métacaractère tel que *
et l’imbriquer dans un autre groupe (de capture ou pas). (?:...)
est particulièrement utile quand vous modifiez des motifs existants, puisque vous pouvez ajouter de nouveaux groupes sans changer la façon dont les autres groupes sont numérotés. Nous devons mentionner ici qu’il n’y a aucune différence de performance dans la recherche de groupes, de capture ou non ; les deux formes travaillent à la même vitesse.
Une fonctionnalité plus importante est le nommage des groupes : au lieu d’y faire référence par des nombres, vous pouvez référencer des groupes par leur nom.
La syntaxe pour nommer les groupes est l’une des extensions spécifiques à Python : (?P<nom>....)`
. nom est, vous vous en doutez, le nom du groupe. Les groupes nommés se comportent exactement comme des groupes de capture, sauf qu’ils associent en plus un nom à un groupe. Les méthodes des objets correspondances qui gèrent les groupes de capture acceptent soit des entiers qui font référence aux numéros des groupes, soit des chaînes de caractères qui désignent les noms des groupes désirés. Les groupes nommés se voient toujours attribuer un numéro, vous pouvez ainsi récupérer les informations d’un groupe de deux façons :
>>> p = re.compile(r'(?P<word>\b\w+\b)')
>>> m = p.search( '(((( Lots of punctuation )))' )
>>> m.group('word')
'Lots'
>>> m.group(1)
'Lots'
Les groupes nommés sont pratiques car il est plus facile de se rappeler un nom qu’un numéro. Voici un exemple de RE tirée du module imaplib
:
InternalDate = re.compile(r'INTERNALDATE "'
r'(?P<day>[ 123][0-9])-(?P<mon>[A-Z][a-z][a-z])-'
r'(?P<year>[0-9][0-9][0-9][0-9])'
r' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])'
r' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])'
r'"')
Il est évidemment plus facile de récupérer m.group('zonem')
que de se rappeler de récupérer le groupe 9.
The syntax for backreferences in an expression such as (...)\1
refers to the
number of the group. There’s naturally a variant that uses the group name
instead of the number. This is another Python extension: (?P=name)
indicates
that the contents of the group called name should again be matched at the
current point. The regular expression for finding doubled words,
(\b\w+)\s+\1
can also be written as (?P<word>\b\w+)\s+(?P=word)
:
>>> p = re.compile(r'(?P<word>\b\w+)\s+(?P=word)')
>>> p.search('Paris in the the spring').group()
'the the'
Assertions prédictives¶
Une autre assertion de largeur zéro est l’assertion prédictive. Une assertion prédictive peut s’exprimer sous deux formes, la positive et la négative, comme ceci :
(?=...)
- Assertion prédictive positive. Elle réussit si l’expression régulière contenue, représentée ici par
...
, correspond effectivement à l’emplacement courant ; dans le cas contraire, elle échoue. Mais, une fois que l’expression contenue a été essayée, le moteur de correspondance n’avance pas ; le reste du motif est testé à l’endroit même où l’assertion a commencé. (?!...)
- Assertion prédictive négative. C’est l’opposée de l’assertion positive ; elle réussit si l’expression régulière contenue ne correspond pas à l’emplacement courant dans la chaine.
Pour rendre ceci plus concret, regardons le cas où une prédiction est utile. Considérons un motif simple qui doit faire correspondre un nom de fichier et le diviser en un nom de base et une extension, séparés par un .
. Par exemple, dans news.rc
, news
est le nom de base et rc
est l’extension du nom de fichier.
Le motif de correspondance est plutôt simple :
.*[.].*$
Notez que le .
doit être traité spécialement car c’est un métacaractère, nous le plaçons donc à l’intérieur d’une classe de caractères pour ne faire correspondre que ce caractère spécifique. Notez également le $
en fin ; nous l’avons ajouté pour nous assurer que tout le reste de la chaîne est bien inclus dans l’extension. Cette expression régulière fait correspondre truc.bar
, autoexec.bat
, sendmail.cf
et printers.conf
.
Maintenant, compliquons un peu le problème ; si nous voulons faire correspondre les noms de fichiers dont l’extension n’est pas bat
? voici quelques tentatives incorrectes :
.*[.][^b].*$
Le premier essai ci-dessus tente d’exclure bat
en spécifiant que le premier caractère de l’extension ne doit pas être b
. Cela ne fonctionne pas, car le motif n’accepte pas truc.bar
.
.*[.]([^b]..|.[^a].|..[^t])$
L’expression devient plus confuse si nous essayons de réparer la première solution en spécifiant l’un des cas suivants : le premier caractère de l’extension n’est pas b
; le deuxième caractère n’est pas a
; ou le troisième caractère n’est pas t`
. Ce motif accepte truc.bar
et rejette autoexec.bat
, mais elle nécessite une extension de trois lettres et n’accepte pas un nom de fichier avec une extension de deux lettres comme sendmail.cf
. Compliquons encore une fois le motif pour essayer de le réparer.
.*[.]([^b].?.?|.[^a]?.?|..?[^t]?)$
Pour cette troisième tentative, les deuxième et troisième lettres sont devenues facultatives afin de permettre la correspondance avec des extensions plus courtes que trois caractères, comme sendmail.cf
.
Le motif devient vraiment compliqué maintenant, ce qui le rend difficile à lire et à comprendre. Pire, si le problème change et que vous voulez exclure à la fois bat
et exe
en tant qu’extensions, le modèle deviendra encore plus compliqué et confus.
Une assertion prédictive négative supprime toute cette confusion :
.*[.](?!bat$)[^.]*$
Cette assertion prédictive négative signifie : si l’expression bat
ne correspond pas à cet emplacement, essaie le reste du motif ; si bat$
correspond, tout le motif échoue. Le $
est nécessaire pour s’assurer que quelque chose comme sample.batch
, où c’est seulement le début de l’extension qui vaut bat
, est autorisé. Le [^...]*
s’assure que le motif fonctionne lorsqu’il y a plusieurs points dans le nom de fichier.
Exclure une autre extension de nom de fichier est maintenant facile ; il suffit de l’ajouter comme alternative à l’intérieur de l’assertion. Le motif suivant exclut les noms de fichiers qui se terminent par bat
ou exe
:
.*[.](?!bat$|exe$)[^.]*$
Modification de chaînes¶
Jusqu’à présent, nous avons simplement effectué des recherches dans une chaîne statique. Les expressions régulières sont aussi couramment utilisées pour modifier les chaînes de caractères de diverses manières, en utilisant les méthodes suivantes des motifs :
Méthode/Attribut | Objectif |
---|---|
split() |
Découpe la chaîne de caractère en liste, la découpant partout où la RE correspond |
sub() |
Recherche toutes les sous-chaînes de caractères où la RE correspond et les substitue par une chaîne de caractères différente |
subn() |
Does the same thing as sub() , but
returns the new string and the number of
replacements |
Découpage de chaînes¶
The split()
method of a pattern splits a string apart
wherever the RE matches, returning a list of the pieces. It’s similar to the
split()
method of strings but provides much more generality in the
delimiters that you can split by; string split()
only supports splitting by
whitespace or by a fixed string. As you’d expect, there’s a module-level
re.split()
function, too.
-
.
split
(string[, maxsplit=0]) Découpe string en suivant les correspondances de l’expression régulière. Si des parenthèses de capture sont utilisées dans la RE, leur contenu est également renvoyé dans la liste résultante. Si maxsplit n’est pas nul, au plus maxsplit découpages sont effectués.
Vous pouvez limiter le nombre de découpages effectués en passant une valeur pour maxsplit. Quand maxsplit n’est pas nul, au plus maxsplit découpages sont effectués et le reste de la chaîne est renvoyé comme dernier élément de la liste. Dans l’exemple suivant, le délimiteur est toute séquence de caractères non alphanumériques.
>>> p = re.compile(r'\W+')
>>> p.split('This is a test, short and sweet, of split().')
['This', 'is', 'a', 'test', 'short', 'and', 'sweet', 'of', 'split', '']
>>> p.split('This is a test, short and sweet, of split().', 3)
['This', 'is', 'a', 'test, short and sweet, of split().']
Parfois, vous voulez récupérer le texte entre les délimiteurs mais aussi quel était le délimiteur. Si des parenthèses de capture sont utilisées dans la RE, leurs valeurs sont également renvoyées dans la liste. Comparons les appels suivants :
>>> p = re.compile(r'\W+')
>>> p2 = re.compile(r'(\W+)')
>>> p.split('This... is a test.')
['This', 'is', 'a', 'test', '']
>>> p2.split('This... is a test.')
['This', '... ', 'is', ' ', 'a', ' ', 'test', '.', '']
La fonction de niveau module re.split()
ajoute la RE à utiliser comme premier argument, mais est par ailleurs identique.
>>> re.split('[\W]+', 'Words, words, words.')
['Words', 'words', 'words', '']
>>> re.split('([\W]+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']
>>> re.split('[\W]+', 'Words, words, words.', 1)
['Words', 'words, words.']
Recherche et substitution¶
Another common task is to find all the matches for a pattern, and replace them
with a different string. The sub()
method takes a replacement value,
which can be either a string or a function, and the string to be processed.
-
.
sub
(replacement, string[, count=0]) Renvoie la chaîne obtenue en remplaçant les occurrences sans chevauchement les plus à gauche de la RE dans string par la substitution replacement. Si le motif n’est pas trouvé, string est renvoyée inchangée.
L’argument optionnel count est le nombre maximum d’occurrences du motif à remplacer ; count doit être un entier positif ou nul. La valeur par défaut de 0 signifie qu’il faut remplacer toutes les occurrences.
Here’s a simple example of using the sub()
method. It replaces colour
names with the word colour
:
>>> p = re.compile('(blue|white|red)')
>>> p.sub('colour', 'blue socks and red shoes')
'colour socks and colour shoes'
>>> p.sub('colour', 'blue socks and red shoes', count=1)
'colour socks and red shoes'
The subn()
method does the same work, but returns a 2-tuple containing the
new string value and the number of replacements that were performed:
>>> p = re.compile('(blue|white|red)')
>>> p.subn('colour', 'blue socks and red shoes')
('colour socks and colour shoes', 2)
>>> p.subn('colour', 'no colours at all')
('no colours at all', 0)
Empty matches are replaced only when they’re not adjacent to a previous match.
>>> p = re.compile('x*')
>>> p.sub('-', 'abxd')
'-a-b-d-'
Si replacement est une chaîne de caractères, toute barre oblique inverse d’échappement est traitée. C’est-à-dire que `\n
est converti en caractère de nouvelle ligne, \r
est converti en retour chariot, et ainsi de suite. Les échappements inconnus comme \&
sont laissés tels quels. Les renvois, tels que \6
, sont remplacés par la sous-chaîne correspondante au groupe dans le RE. Ceci vous permet d’incorporer des parties du texte original dans la chaîne de remplacement résultante.
Cet exemple fait correspondre le mot section
suivi par une chaîne encadrée par {
et }
, et modifie section
en subsection
:
>>> p = re.compile('section{ ( [^}]* ) }', re.VERBOSE)
>>> p.sub(r'subsection{\1}','section{First} section{second}')
'subsection{First} subsection{second}'
Il existe aussi une syntaxe pour faire référence aux groupes nommés définis par la syntaxe (?P<nom>....)
. \g<nom>
utilise la sous-chaîne correspondante au groupe nommé nom
et \g<numéro>
utilise le numéro de groupe correspondant. \g<2>
est donc l’équivalent de \2
, mais n’est pas ambigu dans une chaîne de substitution telle que \g<2>0
(\20
serait interprété comme une référence au groupe 20 et non comme une référence au groupe 2 suivie du caractère littéral '0'
). Les substitutions suivantes sont toutes équivalentes mais utilisent les trois variantes de la chaîne de remplacement.
>>> p = re.compile('section{ (?P<name> [^}]* ) }', re.VERBOSE)
>>> p.sub(r'subsection{\1}','section{First}')
'subsection{First}'
>>> p.sub(r'subsection{\g<1>}','section{First}')
'subsection{First}'
>>> p.sub(r'subsection{\g<name>}','section{First}')
'subsection{First}'
replacement peut aussi être une fonction, ce qui vous donne encore plus de contrôle. Si replacement est une fonction, la fonction est appelée pour chaque occurrence non chevauchante de pattern. À chaque appel, un argument objet correspondance est passé à la fonction, qui peut utiliser cette information pour calculer la chaîne de remplacement désirée et la renvoyer.
Dans l’exemple suivant, la fonction de substitution convertit un nombre décimal en hexadécimal :
>>> def hexrepl(match):
... "Return the hex string for a decimal number"
... value = int(match.group())
... return hex(value)
...
>>> p = re.compile(r'\d+')
>>> p.sub(hexrepl, 'Call 65490 for printing, 49152 for user code.')
'Call 0xffd2 for printing, 0xc000 for user code.'
Quand vous utilisez la fonction de niveau module re.sub()
, le motif est passé comme premier argument. Vous pouvez fournir le motif sous forme d’objet ou de chaîne de caractères ; si vous avez besoin de spécifier des options pour l’expression régulière, vous devez soit utiliser un objet motif comme premier paramètre, soit utiliser des modificateurs intégrés dans la chaîne de caractères, par exemple sub("(?i)b+", "x", "bbbb BBBBB")```renvoie ``'x x'
.
Problèmes classiques¶
Les expressions régulières constituent un outil puissant pour certaines applications mais, à certains égards, leur comportement n’est pas intuitif et, parfois, elles ne se comportent pas comme vous pouvez vous y attendre. Cette section met en évidence certains des pièges les plus courants.
Utilisez les méthodes du type string¶
Sometimes using the re
module is a mistake. If you’re matching a fixed
string, or a single character class, and you’re not using any re
features
such as the IGNORECASE
flag, then the full power of regular expressions
may not be required. Strings have several methods for performing operations with
fixed strings and they’re usually much faster, because the implementation is a
single small C loop that’s been optimized for the purpose, instead of the large,
more generalized regular expression engine.
One example might be replacing a single fixed string with another one; for
example, you might replace word
with deed
. re.sub()
seems like the
function to use for this, but consider the replace()
method. Note that
replace()
will also replace word
inside words, turning swordfish
into sdeedfish
, but the naive RE word
would have done that, too. (To
avoid performing the substitution on parts of words, the pattern would have to
be \bword\b
, in order to require that word
have a word boundary on
either side. This takes the job beyond replace()
”s abilities.)
Another common task is deleting every occurrence of a single character from a
string or replacing it with another single character. You might do this with
something like re.sub('\n', ' ', S)
, but translate()
is capable of
doing both tasks and will be faster than any regular expression operation can
be.
Bref, avant de passer au module re
, évaluez d’abord si votre problème peut être résolu avec une méthode de chaîne plus rapide et plus simple.
match() contre search()¶
The match()
function only checks if the RE matches at the beginning of the
string while search()
will scan forward through the string for a match.
It’s important to keep this distinction in mind. Remember, match()
will
only report a successful match which will start at 0; if the match wouldn’t
start at zero, match()
will not report it.
>>> print(re.match('super', 'superstition').span())
(0, 5)
>>> print(re.match('super', 'insuperable'))
None
On the other hand, search()
will scan forward through the string,
reporting the first match it finds.
>>> print(re.search('super', 'superstition').span())
(0, 5)
>>> print(re.search('super', 'insuperable').span())
(2, 7)
Vous pouvez être tenté d’utiliser re.match()
en ajoutant simplement .*
au début de votre RE. Ce n’est pas une bonne idée, utilisez plutôt re.search()
. Le compilateur d’expressions régulières analyse les REs pour optimiser le processus de recherche d’une correspondance. Cette analyse permet de déterminer ce que doit être le premier caractère d’une correspondance ; par exemple, un motif commençant par Corbeau
doit faire correspondre un 'C'
en tête. L’analyse permet au moteur de parcourir rapidement la chaîne de caractères à la recherche du caractère de départ, n’essayant la correspondance complète que si un « C » a déjà été trouvé.
Ajouter .*
annihile cette optimisation, nécessitant un balayage jusqu’à la fin de la chaîne de caractères, puis un retour en arrière pour trouver une correspondance pour le reste de la RE. Préférez l’utilisation re.search()
.
Glouton contre non-glouton¶
Si vous répétez un motif dans une expression régulière, comme a*
, l’action résultante est de consommer autant de motifs que possible. C’est un problème lorsque vous essayez de faire correspondre une paire de délimiteurs, comme des chevrons encadrant une balise HTML. Le motif naïf pour faire correspondre une seule balise HTML ne fonctionne pas en raison de la nature gloutonne de .*
.
>>> s = '<html><head><title>Title</title>'
>>> len(s)
32
>>> print(re.match('<.*>', s).span())
(0, 32)
>>> print(re.match('<.*>', s).group())
<html><head><title>Title</title>
The RE matches the '<'
in <html>
, and the .*
consumes the rest of
the string. There’s still more left in the RE, though, and the >
can’t
match at the end of the string, so the regular expression engine has to
backtrack character by character until it finds a match for the >
. The
final match extends from the '<'
in <html>
to the '>'
in
</title>
, which isn’t what you want.
Dans ce cas, la solution consiste à utiliser des quantificateurs non gloutons tels que *?
, +?
, ??
ou {m,n}?
, qui effectuent une correspondance aussi petite que possible. Dans l’exemple ci-dessus, le '>'
est essayé immédiatement après que le '<'
corresponde et, s’il échoue, le moteur avance caractère par caractère, ré-essayant '>'
à chaque pas. Nous obtenons alors le bon résultat :
>>> print(re.match('<.*?>', s).group())
<html>
Note : l’analyse du HTML ou du XML avec des expressions régulières est tout sauf une sinécure. Les motifs écrits à la va-vite traiteront les cas communs, mais HTML et XML ont des cas spéciaux qui font planter l’expression régulière évidente ; quand vous aurez écrit une expression régulière qui traite tous les cas possibles, les motifs seront très compliqués. Utilisez un module d’analyse HTML ou XML pour de telles tâches.
Utilisez re.VERBOSE¶
À présent, vous vous êtes rendu compte que les expressions régulières sont une notation très compacte, mais qu’elles ne sont pas très lisibles. Une RE modérément complexe peut rapidement devenir une longue collection de barres obliques inverses, de parenthèses et de métacaractères, ce qui la rend difficile à lire et à comprendre.
For such REs, specifying the re.VERBOSE
flag when compiling the regular
expression can be helpful, because it allows you to format the regular
expression more clearly.
L’option re.VERBOSE
a plusieurs effets. Les espaces dans l’expression régulière qui ne sont pas à l’intérieur d’une classe de caractères sont ignorées. Cela signifie qu’une expression comme chien | chat
est équivalente à chien|chat
qui est moins lisible, mais [a b]
correspond toujours aux caractères 'a'
, 'b'
ou à une espace. En outre, vous avez la possibilité de mettre des commentaires à l’intérieur d’une RE ; les commentaires s’étendent du caractère #
à la nouvelle ligne suivante. Lorsque vous l’utilisez avec des chaînes à triple guillemets, cela permet aux RE d’être formatées plus proprement :
pat = re.compile(r"""
\s* # Skip leading whitespace
(?P<header>[^:]+) # Header name
\s* : # Whitespace, and a colon
(?P<value>.*?) # The header's value -- *? used to
# lose the following trailing whitespace
\s*$ # Trailing whitespace to end-of-line
""", re.VERBOSE)
Ceci est beaucoup plus lisible que:
pat = re.compile(r"\s*(?P<header>[^:]+)\s*:(?P<value>.*?)\s*$")
Vos commentaires¶
Les expressions régulières sont un sujet compliqué. Est-ce que ce document vous a aidé à les comprendre ? Des parties ne sont pas claires, ou des problèmes que vous avez rencontrés ne sont pas traités ici ? Si tel est le cas, merci d’envoyer vos suggestions d’améliorations à l’auteur.
The most complete book on regular expressions is almost certainly Jeffrey
Friedl’s Mastering Regular Expressions, published by O’Reilly. Unfortunately,
it exclusively concentrates on Perl and Java’s flavours of regular expressions,
and doesn’t contain any Python material at all, so it won’t be useful as a
reference for programming in Python. (The first edition covered Python’s
now-removed regex
module, which won’t help you much.) Consider checking
it out from your library.