Expressions Régulières

Expressions régulières

Une expression régulière (raccourcie en regex [...]) est une séquence de caractères qui spécifie un modèle de recherche dans un texte. [...] utilisée par les algorithmes de recherche de chaînes pour les opérations de "trouver" ou de "trouver et remplacer" sur des chaînes, ou pour la validation des entrées.

  1. Importez le module regex avec import re.
  2. Créez un objet Regex avec la fonction re.compile(). (N’oubliez pas d’utiliser une chaîne brute.)
  3. Passez la chaîne que vous souhaitez rechercher à la méthode search() de l’objet Regex. Cela renvoie un objet Match.
  4. Appelez la méthode group() de l’objet Match pour renvoyer une chaîne du texte réellement correspondant.

Toutes les fonctions regex en Python se trouvent dans le module re :

# Importez le module re pour les opérations d'expression régulière
import re

Symboles Regex

SymboleCorrespond à
?zéro ou une fois du groupe précédent.
*zéro ou plusieurs fois du groupe précédent.
+une ou plusieurs fois du groupe précédent.
{n}exactement n fois du groupe précédent.
{n,}n fois ou plus du groupe précédent.
{,m}0 à m fois du groupe précédent.
{n,m}au moins n et au plus m fois du p précédent.
{n,m}? ou *? ou +?effectue une correspondance non gourmande du p précédent.
^spamsignifie que la chaîne doit commencer par spam.
spam$signifie que la chaîne doit se terminer par spam.
.tout caractère, sauf les caractères de nouvelle ligne.
\d, \w, et \sun chiffre, un mot, ou un caractère d’espace, respectivement.
\D, \W, et \Stout sauf un chiffre, un mot, ou un espace, respectivement.
[abc]tout caractère entre les crochets (comme a, b, ).
[^abc]tout caractère qui n’est pas entre les crochets.

Objets regex correspondants

# re.compile(): créer un objet de motif regex (utiliser une chaîne brute r'' pour éviter l'échappement)
phone_num_regex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')  # Motif : 3 chiffres-3 chiffres-4 chiffres

mo = phone_num_regex.search('My number is 415-555-4242.')  # Rechercher le motif

print(f'Phone number found: {mo.group()}')  # group() renvoie le texte correspondant
Phone number found: 415-555-4242

Groupement avec parenthèses

# Les parenthèses créent des groupes : group(1) renvoie le premier groupe, group(2) renvoie le deuxième
phone_num_regex = re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)')  # Deux groupes entre parenthèses
mo = phone_num_regex.search('My number is 415-555-4242.')

mo.group(1)  # Renvoie le premier groupe : '415'
'415'
mo.group(2)
'555-4242'
mo.group(0)
'415-555-4242'
mo.group()
'415-555-4242'
Quiz

Connectez-vous pour répondre à ce quiz et suivre votre progression d'apprentissage

Que renvoie group() lorsqu'il est appelé sur un objet match ?
A. Le texte correspondant entier
B. Le premier groupe uniquement
C. Tous les groupes sous forme de liste
D. L'index de la correspondance

Pour récupérer tous les groupes en une seule fois, utilisez la méthode groups() :

# groups(): renvoie un tuple de tous les groupes
mo.groups()  # Renvoie ('415', '555-4242')
('415', '555-4242')
area_code, main_number = mo.groups()

print(area_code)
415
print(main_number)
555-4242

Groupement multiple avec Pipe

Vous pouvez utiliser le caractère | partout où vous souhaitez faire correspondre une de plusieurs expressions.

hero_regex = re.compile (r'Batman|Tina Fey')

mo1 = hero_regex.search('Batman and Tina Fey.')
mo1.group()
'Batman'
mo2 = hero_regex.search('Tina Fey and Batman.')
mo2.group()
'Tina Fey'

Vous pouvez également utiliser le pipe pour faire correspondre un de plusieurs motifs dans le cadre de votre regex :

bat_regex = re.compile(r'Bat(man|mobile|copter|bat)')
mo = bat_regex.search('Batmobile lost a wheel')

mo.group()
'Batmobile'
mo.group(1)
'mobile'

Correspondance optionnelle avec le point d’interrogation

Le caractère ? signale le groupe qui le précède comme étant une partie optionnelle du motif.

bat_regex = re.compile(r'Bat(wo)?man')

mo1 = bat_regex.search('The Adventures of Batman')
mo1.group()
'Batman'
mo2 = bat_regex.search('The Adventures of Batwoman')
mo2.group()
'Batwoman'

Correspondance zéro ou plusieurs fois avec l’étoile

Le * (étoile ou astérisque) signifie “faire correspondre zéro ou plusieurs fois”. Le groupe précédant l’étoile peut apparaître n’importe quel nombre de fois dans le texte.

bat_regex = re.compile(r'Bat(wo)*man')
mo1 = bat_regex.search('The Adventures of Batman')
mo1.group()
'Batman'
mo2 = bat_regex.search('The Adventures of Batwoman')
mo2.group()
'Batwoman'
mo3 = bat_regex.search('The Adventures of Batwowowowoman')
mo3.group()
'Batwowowowoman'

Correspondance une ou plusieurs fois avec le Plus

Le + (ou plus) signifie faire correspondre une ou plusieurs fois. Le groupe précédant un plus doit apparaître au moins une fois :

bat_regex = re.compile(r'Bat(wo)+man')

mo1 = bat_regex.search('The Adventures of Batwoman')
mo1.group()
'Batwoman'
mo2 = bat_regex.search('The Adventures of Batwowowowoman')
mo2.group()
'Batwowowowoman'
mo3 = bat_regex.search('The Adventures of Batman')
mo3 is None
True

Correspondance de répétitions spécifiques avec les accolades

Si vous avez un groupe que vous souhaitez répéter un nombre spécifique de fois, suivez le groupe dans votre regex avec un nombre entre accolades :

ha_regex = re.compile(r'(Ha){3}')

mo1 = ha_regex.search('HaHaHa')
mo1.group()
'HaHaHa'
mo2 = ha_regex.search('Ha')
mo2 is None
True

Au lieu d’un seul nombre, vous pouvez spécifier une plage avec un minimum et un maximum entre les accolades. Par exemple, la regex (Ha){3,5} correspondra à ‘HaHaHa’, ‘HaHaHaHa’ et ‘HaHaHaHaHa’.

ha_regex = re.compile(r'(Ha){2,3}')
mo1 = ha_regex.search('HaHaHaHa')
mo1.group()
'HaHaHa'

Correspondance gourmande et non gourmande

Les expressions régulières de Python sont gourmandes par défaut : dans les situations ambiguës, elles correspondent à la chaîne la plus longue possible. La version non gourmande des accolades, qui correspond à la chaîne la plus courte possible, a l’accolade fermante suivie d’un point d’interrogation.

greedy_ha_regex = re.compile(r'(Ha){3,5}')

mo1 = greedy_ha_regex.search('HaHaHaHaHa')
mo1.group()
'HaHaHaHaHa'
non_greedy_ha_regex = re.compile(r'(Ha){3,5}?')
mo2 = non_greedy_ha_regex.search('HaHaHaHaHa')
mo2.group()
'HaHaHa'
Quiz

Connectez-vous pour répondre à ce quiz et suivre votre progression d'apprentissage

Qu'est-ce qui rend un motif regex non gourmand ?
A. Utiliser _ au lieu de +
B. Ajouter un ? après le quantificateur (ex : _?, +?, {3,5}?)
C. Utiliser des parenthèses
D. Utiliser des crochets

La méthode findall()

La méthode findall() renverra les chaînes de chaque correspondance dans la chaîne recherchée.

phone_num_regex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d') # n'a pas de groupes

phone_num_regex.findall('Cell: 415-555-9999 Work: 212-555-0000')
['415-555-9999', '212-555-0000']

Créer vos propres classes de caractères

Vous pouvez définir votre propre classe de caractères en utilisant des crochets. Par exemple, la classe de caractères [aeiouAEIOU] correspondra à n’importe quelle voyelle, minuscule et majuscule.

vowel_regex = re.compile(r'[aeiouAEIOU]')
vowel_regex.findall('Robocop eats baby food. BABY FOOD.')
['o', 'o', 'o', 'e', 'a', 'a', 'o', 'o', 'A', 'O', 'O']

Vous pouvez également inclure des plages de lettres ou de chiffres en utilisant un trait d’union. Par exemple, la classe de caractères [a-zA-Z0-9] correspondra à toutes les lettres minuscules, majuscules et aux chiffres.

En plaçant un caractère circonflexe (^) juste après le crochet ouvrant de la classe de caractères, vous pouvez créer une classe de caractères négative qui correspondra à tous les caractères qui ne sont pas dans la classe de caractères :

consonant_regex = re.compile(r'[^aeiouAEIOU]')
consonant_regex.findall('Robocop eats baby food. BABY FOOD.')
['R', 'b', 'c', 'p', ' ', 't', 's', ' ', 'b', 'b', 'y', ' ', 'f', 'd', '.', ' ', 'B', 'B', 'Y', ' ', 'F', 'D', '.']

Les caractères Caret et Dollar sign

  • Vous pouvez également utiliser le symbole circonflexe ^ au début d’une regex pour indiquer qu’une correspondance doit se produire au début du texte recherché.

  • De même, vous pouvez placer un signe dollar $ à la fin de la regex pour indiquer que la chaîne doit se terminer par ce motif regex.

  • Et vous pouvez utiliser le ^ et le $ ensemble pour indiquer que la chaîne entière doit correspondre à la regex.

La chaîne d’expression régulière r'^Hello' correspond aux chaînes qui commencent par ‘Hello’ :

begins_with_hello = re.compile(r'^Hello')
begins_with_hello.search('Hello world!')
<_sre.SRE_Match object; span=(0, 5), match='Hello'>
begins_with_hello.search('He said hello.') is None
True

La chaîne d’expression régulière r'\d\$' correspond aux chaînes qui se terminent par un caractère numérique de 0 à 9 :

whole_string_is_num = re.compile(r'^\d+$')

whole_string_is_num.search('1234567890')
<_sre.SRE_Match object; span=(0, 10), match='1234567890'>
whole_string_is_num.search('12345xyz67890') is None
True
whole_string_is_num.search('12 34567890') is None
True

Le caractère Joker

Le caractère . (ou point) dans une expression régulière correspondra à tout caractère sauf une nouvelle ligne :

at_regex = re.compile(r'.at')

at_regex.findall('The cat in the hat sat on the flat mat.')
['cat', 'hat', 'sat', 'lat', 'mat']

Correspondance de tout avec Dot-Star

name_regex = re.compile(r'First Name: (.*) Last Name: (.*)')

mo = name_regex.search('First Name: Al Last Name: Sweigart')
mo.group(1)
'Al'
mo.group(2)
'Sweigart'

Le .* utilise le mode gourmand : il essaiera toujours de correspondre au texte le plus long possible. Pour faire correspondre n’importe quel texte de manière non gourmande, utilisez le point, l’étoile et le point d’interrogation (.*?). Le point d’interrogation indique à Python de faire correspondre de manière non gourmande :

non_greedy_regex = re.compile(r'<.*?>')
mo = non_greedy_regex.search('<To serve man> for dinner.>')
mo.group()
'<To serve man>'
greedy_regex = re.compile(r'<.*>')
mo = greedy_regex.search('<To serve man> for dinner.>')
mo.group()
'<To serve man> for dinner.>'

Correspondance des nouvelles lignes avec le caractère Point

Le point-étoile correspond à tout sauf une nouvelle ligne. En passant re.DOTALL comme deuxième argument à re.compile(), vous pouvez faire en sorte que le caractère point corresponde à tous les caractères, y compris le caractère de nouvelle ligne :

no_newline_regex = re.compile('.*')
no_newline_regex.search('Serve the public trust.\nProtect the innocent.\nUphold the law.').group()
'Serve the public trust.'
newline_regex = re.compile('.*', re.DOTALL)
newline_regex.search('Serve the public trust.\nProtect the innocent.\nUphold the law.').group()
'Serve the public trust.\nProtect the innocent.\nUphold the law.'

Correspondance insensible à la casse

Pour rendre votre regex insensible à la casse, vous pouvez passer re.IGNORECASE ou re.I comme deuxième argument à re.compile() :

robocop = re.compile(r'robocop', re.I)

robocop.search('Robocop is part man, part machine, all cop.').group()
'Robocop'
robocop.search('ROBOCOP protects the innocent.').group()
'ROBOCOP'
robocop.search('Al, why does your programming book talk about robocop so much?').group()
'robocop'

Substitution de chaînes avec la méthode sub()

La méthode sub() pour les objets Regex reçoit deux arguments :

  1. Le premier argument est une chaîne pour remplacer toutes les correspondances.
  2. Le second est la chaîne pour l’expression régulière.

La méthode sub() renvoie une chaîne avec les substitutions appliquées :

names_regex = re.compile(r'Agent \w+')

names_regex.sub('CENSORED', 'Agent Alice gave the secret documents to Agent Bob.')
'CENSORED gave the secret documents to CENSORED.'
Quiz

Connectez-vous pour répondre à ce quiz et suivre votre progression d'apprentissage

Que fait la méthode sub() ?
A. Trouve toutes les correspondances dans une chaîne
B. Remplace toutes les correspondances par une chaîne de remplacement
C. Divise une chaîne aux correspondances
D. Valide le format d'une chaîne

Gestion des Regex complexes

Pour indiquer à la fonction re.compile() d’ignorer les espaces et les commentaires à l’intérieur de la chaîne d’expression régulière, le “mode verbeux” peut être activé en passant la variable re.VERBOSE comme deuxième argument à re.compile().

Maintenant, au lieu d’une expression régulière difficile à lire comme celle-ci :

phone_regex = re.compile(r'((\d{3}|\(\d{3}\))?(\s|-|\.)?\d{3}(\s|-|\.)\d{4}(\s*(ext|x|ext.)\s*\d{2,5})?)')

vous pouvez répartir l’expression régulière sur plusieurs lignes avec des commentaires comme ceci :

phone_regex = re.compile(r'''(
    (\d{3}|\(\d{3}\))?            # indicatif régional
    (\s|-|\.)?                    # séparateur
    \d{3}                         # 3 premiers chiffres
    (\s|-|\.)                     # séparateur
    \d{4}                         # 4 derniers chiffres
    (\s*(ext|x|ext.)\s*\d{2,5})?  # extension
    )''', re.VERBOSE)
Quiz

Connectez-vous pour répondre à ce quiz et suivre votre progression d'apprentissage

Que fait re.VERBOSE lorsqu'il est passé à re.compile() ?
A. Permet les espaces et les commentaires dans le motif regex pour une meilleure lisibilité
B. Rend le regex insensible à la casse
C. Fait correspondre le point aux caractères de nouvelle ligne
D. Accélère la correspondance regex

Liens pertinents