Python - Expressions Régulières

Bonjour à tous, futurs sorciers Python ! Aujourd'hui, nous allons entreprendre un voyage passionnant dans le monde des expressions régulières (regex) en Python. Ne vous inquiétez pas si vous n'avez jamais entendu parler de regex avant - à la fin de ce tutoriel, vous manipulerez cette puissante arme comme un pro !

Python - Reg Expressions

Qu'est-ce qu'une Expression Régulière ?

Avant de plonger dans les détails, voyons ce qu'ont sont les expressions régulières. Imaginez-vous en tant que détective cherchant un motif spécifique dans un océan de texte. Les expressions régulières sont votre loupe, vous aidant à rechercher et à manipuler des chaînes de caractères en fonction de motifs. C'est assez cool, non ?

Chaînes Brutes

En Python, lorsque vous travaillez avec des regex, vous utilisez souvent des chaînes brutes. Celles-ci sont préfixées par un 'r' et traitent les barres obliques inverses comme des caractères littéraux. C'est particulièrement utile dans les regex car les barres obliques inverses sont courantes.

# Chaîne normale
print("Bonjour\nMonde")
# Chaîne brute
print(r"Bonjour\nMonde")

Dans le premier cas, vous verrez "Bonjour" et "Monde" sur des lignes séparées. Dans le second, vous verrez "Bonjour\nMonde" tel quel. Cela devient crucial lorsque vous travaillez avec des motifs regex.

Méta-caractères

Les méta-caractères sont les briques de base des regex. Ils ont des significations spéciales et nous aident à définir des motifs. Voici quelques-uns des plus courants :

Méta-caractère Signification
. Correspond à n'importe quel caractère sauf le saut de ligne
^ Correspond au début d'une chaîne
$ Correspond à la fin d'une chaîne
* Correspond à 0 ou plusieurs répétitions
+ Correspond à 1 ou plusieurs répétitions
? Correspond à 0 ou 1 répétition
{} Correspond à un nombre de répétitions spécifié explicitement
[] Spécifie un ensemble de caractères à correspondre
\ Échappe les caractères spéciaux

La Fonction re.match()

La fonction re.match() tente de faire correspondre un motif au début d'une chaîne. Si elle trouve une correspondance, elle renvoie un objet de correspondance ; sinon, elle renvoie None.

import re

résultat = re.match(r"Bonjour", "Bonjour, Monde !")
if résultat:
print("Correspondance trouvée :", résultat.group())
else:
print("Aucune correspondance")

Cela imprime "Correspondance trouvée : Bonjour". La méthode group() renvoie la sous-chaîne correspondante.

La Fonction re.search()

Alors que re.match() recherche une correspondance au début de la chaîne, re.search() scanne toute la chaîne pour trouver une correspondance.

import re

résultat = re.search(r"Monde", "Bonjour, Monde !")
if résultat:
print("Correspondance trouvée :", résultat.group())
else:
print("Aucune correspondance")

Cela imprime "Correspondance trouvée : Monde".

Correspondance vs Recherche

La différence principale entre match() et search() est que match() vérifie la correspondance uniquement au début de la chaîne, tandis que search() vérifie la correspondance n'importe où dans la chaîne.

La Fonction re.findall()

La fonction re.findall() renvoie toutes les correspondances non chevauchantes d'un motif dans une chaîne sous forme de liste.

import re

texte = "La pluie en Espagne tombe principalement dans la plaine"
résultat = re.findall(r"ain", texte)
print(résultat)

Cela imprime ['ain', 'ain', 'ain'].

La Fonction re.sub()

La fonction re.sub() remplace toutes les occurrences d'un motif dans une chaîne par une chaîne de remplacement.

import re

texte = "La pluie en Espagne"
résultat = re.sub(r"a", "o", texte)
print(résultat)

Cela imprime "La pluie en Spoine".

La Fonction re.compile()

La fonction re.compile() crée un objet regex pour réutilisation, ce qui peut être plus efficace si vous utilisez le même motif plusieurs fois.

import re

motif = re.compile(r"\d+")
résultat1 = motif.findall("Il y a 123 pommes et 456 oranges")
résultat2 = motif.findall("J'ai 789 bananes")

print(résultat1)
print(résultat2)

Cela imprime ['123', '456'] et ['789'].

La Fonction re.finditer()

La fonction re.finditer() renvoie un itérateur produisant des objets de correspondance pour toutes les correspondances non chevauchantes d'un motif dans une chaîne.

import re

texte = "La pluie en Espagne"
for correspondance in re.finditer(r"ain", texte):
print(f"Trouvé '{correspondance.group()}' à la position {correspondance.start()}-{correspondance.end()}")

Cela imprime :

Trouvé 'ain' à la position 5-8
Trouvé 'ain' à la position 17-20

Cas d'Utilisation des Expressions Régulières en Python

Les expressions régulières ont de nombreuses applications pratiques. Examinons un cas d'utilisation courant :

Trouver des mots commençant par des voyelles

import re

texte = "Un pomme par jour garde le médecin loin"
mots_voyelles = re.findall(r'\b[aeiouAEIOU]\w+', texte)
print(mots_voyelles)

Cela imprime ['Un', 'pomme', 'a', 'loin'].

Modificateurs d'Expression Régulière : Options d'Indicateurs

Le module re de Python fournit plusieurs indicateurs d'option qui modifient la manière dont les motifs sont interprétés :

Indicateur Description
re.IGNORECASE (re.I) Effectue une correspondance insensible à la casse
re.MULTILINE (re.M) Fait correspondre ^ au début de chaque ligne et $ à la fin de chaque ligne
re.DOTALL (re.S) Fait correspondre . à n'importe quel caractère, y compris le saut de ligne
re.VERBOSE (re.X) Vous permet d'écrire des motifs regex plus lisibles

Motifs d'Expressions Régulières

Explorons quelques motifs plus avancés :

Classes de Caractères

Les classes de caractères vous permettent de spécifier un ensemble de caractères à faire correspondre :

import re

texte = "Le rapide布朗 fox saute par-dessus le chien paresseux"
résultat = re.findall(r"[aeiou]", texte)
print(résultat)

Cela imprime toutes les voyelles trouvées dans le texte.

Classes de Caractères Spéciaux

Python regex prend en charge les classes de caractères spéciaux :

Classe Description
\d Correspond à n'importe quel chiffre décimal
\D Correspond à n'importe quel caractère non numérique
\s Correspond à n'importe quel caractère d'espacement
\S Correspond à n'importe quel caractère non d'espacement
\w Correspond à n'importe quel caractère alphanumérique
\W Correspond à n'importe quel caractère non alphanumérique

Cas de Répétition

Nous pouvons spécifier combien de fois un motif doit apparaître :

import re

texte = "J'ai 111 pommes et 22 oranges"
résultat = re.findall(r"\d{2,3}", texte)
print(résultat)

Cela imprime ['111', '22'], correspondant aux nombres avec 2 ou 3 chiffres.

Répétition Non Gourmande

Par défaut, la répétition est gourmande, ce qui signifie qu'elle correspond au plus possible. Ajouter un ? après la répétition la rend non gourmande :

import re

texte = "<h1>Titre</h1><p>Paragraphe</p>"
gourmand = re.findall(r"<.*>", texte)
non_gourmand = re.findall(r"<.*?>", texte)
print("Gourmand :", gourmand)
print("Non gourmand :", non_gourmand)

Cela montrera la différence entre les correspondances gourmandes et non gourmandes.

Groupement avec Parenthèses

Les parenthèses permettent de regrouper des parties de la regex :

import re

texte = "John Smith ([email protected])"
résultat = re.search(r"(\w+) (\w+) \((\w+@\w+\.\w+)\)", texte)
if résultat:
print(f"Nom Complet: {résultat.group(1)} {résultat.group(2)}")
print(f"Email: {résultat.group(3)}")

Cela extrait le nom et l'email du texte.

Références Arrière

Les références arrière permettent de faire référence aux groupes correspondants précédents :

import re

texte = "<h1>Titre</h1><p>Paragraphe</p>"
résultat = re.findall(r"<(\w+)>.*?</\1>", texte)
print(résultat)

Cela correspond aux balises HTML ouvrantes et fermantes.

Alternatives

Le caractère | permet de spécifier des alternatives :

import re

texte = "La couleur du ciel est bleu ou gris"
résultat = re.search(r"bleu|gris", texte)
if résultat:
print(f"Couleur trouvée: {résultat.group()}")

Cela correspond soit à "bleu" soit à "gris".

Ancres

Les ancres spécifient des positions dans le texte :

import re

texte = "Python est génial"
début = re.match(r"^Python", texte)
fin = re.search(r"génial$", texte)
print(f"Commence par Python: {bool(début)}")
print(f"Se termine par génial: {bool(fin)}")

Cela vérifie si le texte commence par "Python" et se termine par "génial".

Syntaxe Spéciale avec Parenthèses

Les parenthèses peuvent être utilisées pour plus que le simple groupement :

  • (?:...) crée un groupe non capturant
  • (?P...) crée un groupe nommé
  • (?=...) crée un lookahead positif
  • (?!...) crée un lookahead négatif
import re

texte = "Python version 3.9.5"
résultat = re.search(r"Python (?:version )?(?P<version>\d+\.\d+\.\d+)", texte)
if résultat:
print(f"Version: {résultat.group('version')}")

Cela extrait le numéro de version, que le mot "version" soit présent ou non dans le texte.

Et voilà, mes amis ! Nous avons parcouru la terre des expressions régulières en Python, des concepts de base aux concepts plus avancés. Rappelez-vous, comme tout outil puissant, les regex nécessitent de la pratique pour être maîtrisés. Alors ne vous découragez pas si cela vous semble Overwhelming au début. Continuez à expérimenter, et bientôt vous trouverez des motifs comme un détective pro ! Bon codage !

Credits: Image by storyset