Contenu

Introduction aux expressions régulières

Introduction

Les expressions régulières (ou regex) sont des séquences de caractères qui décrivent un modèle de recherche dans une chaîne de caractères. Elles sont utilisées dans de nombreux langages de programmation pour rechercher, extraire et manipuler des données textuelles. Les regex permettent de décrire avec précision des modèles de recherche complexes, tels que des adresses e-mail ou des numéros de téléphone, et peuvent être bien plus rapides et efficaces que de parcourir manuellement chaque ligne de texte pour extraire des informations spécifiques.

Dans cet article, nous allons explorer les différentes fonctionnalités des expressions régulières, en nous concentrant sur les exemples pratiques en Python, Bash et JavaScript. Nous allons commencer par les bases des expressions régulières, puis progresser vers des techniques plus avancées comme les lookaround, les quantificateurs et l’alternance. Enfin, nous examinerons comment utiliser les expressions régulières pour effectuer des substitutions et modifier le comportement de recherche en utilisant des drapeaux.

Que vous soyez un développeur débutant ou expérimenté, cet article devrait vous fournir des bases solides pour comprendre et utiliser les expressions régulières dans vos projets.


Voici un exemple d’expression régulière simple:

1
abc+

Dans cet exemple, l’expression régulière abc+ correspond à la chaîne “ab” suivie d’un ou plusieurs caractères “c”. Cela signifie que l’expression régulière correspondra à “abc”, “abcc”, “abccc”, etc.



Les classes de caractères

Les classes de caractères (ou character classes) sont des ensembles de caractères qui peuvent être utilisés pour décrire des modèles de recherche spécifiques. Les classes de caractères sont spécifiées en utilisant des crochets ([]) et permettent de décrire un seul caractère qui correspond à n’importe quel caractère de l’ensemble spécifié. Voici quelques exemples de classes de caractères :

  • [abc] : correspond à n’importe quel caractère qui est soit ‘a’, ‘b’, ou ‘c’
  • [a-z] : correspond à n’importe quel caractère qui est une lettre minuscule
  • [A-Z] : correspond à n’importe quel caractère qui est une lettre majuscule
  • [0-9] : correspond à n’importe quel caractère qui est un chiffre
  • . : correspond à n’importe quel caractère

Les classes de caractères peuvent également être négatives, ce qui signifie qu’elles correspondent à tout caractère qui n’appartient pas à l’ensemble spécifié. Les classes de caractères négatives sont spécifiées en utilisant le symbole ^ à l’intérieur des crochets.

Voici un exemple de classe de caractères négative :

  • [^0-9] : correspond à n’importe quel caractère qui n’est pas un chiffre

Il est également possible de combiner des classes de caractères en utilisant des parenthèses pour créer des groupes. Les groupes peuvent être combinés avec des quantificateurs pour rechercher des motifs de texte plus complexes.

Voici un exemple de groupe de classes de caractères :

  • ([a-z]|[A-Z]) : correspond à n’importe quel caractère qui est une lettre, qu’elle soit majuscule ou minuscule

En Python, les classes de caractères peuvent être utilisées en utilisant la fonction re.search() :

1
2
3
4
5
6
7
8
9
import re

text = "The quick brown fox jumps over the lazy dog."
match = re.search("[aeiou]", text)

if match:
    print("Le premier voyelle trouvée est :", match.group())
else:
    print("Aucune voyelle trouvée.")

En JavaScript, les classes de caractères peuvent être utilisées en utilisant la méthode test() de l’objet RegExp :

1
2
3
4
5
6
7
8
const text = "The quick brown fox jumps over the lazy dog.";
const match = /[aeiou]/.test(text);

if (match) {
    console.log("Une voyelle a été trouvée.");
} else {
    console.log("Aucune voyelle trouvée.");
}

En Bash, les classes de caractères peuvent être utilisées en utilisant l’outil grep :

1
2
3
4
5
6
7
8
text="The quick brown fox jumps over the lazy dog."
match=$(echo $text | grep "[aeiou]")

if [ -n "$match" ]; then
    echo "Une voyelle a été trouvée."
else
    echo "Aucune voyelle trouvée."
fi


Ancres

Les ancres sont des métacaractères qui permettent de délimiter des motifs de recherche en spécifiant une position précise dans le texte. Les deux ancres les plus couramment utilisées sont ^ et $.


L’ancre ^ correspond au début d’une chaine de caractère, tandis que l’ancre $ correspond à la fin d’une chaine de caractère.

Ils peuvent également correspondre au début et à la fin d’une ligne si le flag multiligne m est activé. Nous reviendrons sur la notion de flag plus tard dans l’article.

Il existe également deux autres ancres, \b et \B, qui correspondent respectivement au début ou à la fin d’un mot.

En voici quelques exemples :

  • \bcode : correspond à toutes les occurrences du mot “code” en début de mot
  • tuto\B : correspond à toutes les occurrences du mot “tuto” qui ne se terminent pas par une lettre

Voici quelques exemples :

  • ^Bonjour : correspond à toutes les chaines de caractères qui commencent par “Bonjour”
  • Au revoir$ : correspond à toutes les chaines de caractères qui se terminent par “Au revoir”

En Python, les ancres peuvent être utilisées en utilisant la fonction re.search() :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import re

text = "Bonjour tout le monde.\nComment ça va ?\nAu revoir !"
match = re.search("^Bonjour", text)
if match:
    print("Le texte commence par 'Bonjour'")

match = re.search("Au revoir$", text)
if match:
    print("Le texte se termine par 'Au revoir'")

En JavaScript, les ancres peuvent être utilisées en utilisant la méthode test() de l’objet RegExp :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const text = "Bonjour tout le monde.\nComment ça va ?\nAu revoir !";
const regex1 = /^Bonjour/;
const regex2 = /Au revoir$/;

if (regex1.test(text)) {
    console.log("Le texte commence par 'Bonjour'");
}

if (regex2.test(text)) {
    console.log("Le texte se termine par 'Au revoir'");
}

En Bash, les ancres peuvent être utilisées en utilisant l’outil grep :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
text="Bonjour tout le monde.
Comment ça va ?
Au revoir !"

if echo "$text" | grep -q "^Bonjour"; then
    echo "Le texte commence par 'Bonjour'"
fi

if echo "$text" | grep -q "Au revoir$"; then
    echo "Le texte se termine par 'Au revoir'"
fi

Remarque, en bash, le grep s’applique sur chaque ligne, il n’y a donc pas de flag multiligne m.



Les caractères échappés / escaped characters

Certains caractères ont un sens particulier dans les expressions régulières et ne peuvent pas être utilisés directement. Pour les utiliser dans une expression régulière, ils doivent être échappés en les préfixant avec un caractère d’échappement (\). Voici quelques exemples de caractères échappés couramment utilisés :

  • \. : correspond à un point littéral
  • \\ : correspond à un backslash littéral
  • \d : correspond à n’importe quel chiffre
  • \D : correspond à n’importe quel caractère qui n’est pas un chiffre
  • \w : correspond à n’importe quel caractère alphanumérique, y compris le soulignement
  • \W : correspond à n’importe quel caractère qui n’est pas alphanumérique ni souligné
  • \s : correspond à n’importe quel espace blanc (espace, tabulation, retour à la ligne, etc.)
  • \S : correspond à n’importe quel caractère qui n’est pas un espace blanc
  • \b : correspond au début d’un mot
  • \B : correspond à la fin d’un mot

En Python, les caractères échappés peuvent être utilisés en utilisant la fonction re.search() :

1
2
3
4
5
6
7
8
9
import re

text = "Le prix est de 5€."
match = re.search("\d", text)

if match:
    print("Le prix est de :", match.group(), "euros.")
else:
    print("Aucun prix trouvé.")

En JavaScript, les caractères échappés peuvent être utilisés en utilisant la méthode test() de l’objet RegExp :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const text = "Le prix est de 5€.";
const re = /\d/
const match = re.exec(text)

console.log(match)
if (match) {
    console.log("Le prix est de : " + match[0] + " euros.");
} else {
    console.log("Aucun prix trouvé.");
}

En Bash, les caractères échappés peuvent être utilisés en utilisant l’outil grep :

1
2
3
4
5
6
7
8
text="Le prix est de 5€."
match=$(echo $text | grep -Po "\d")

if [ -n "$match" ]; then
    echo "Le prix est de : $match euros."
else
    echo "Aucun prix trouvé."
fi

-o: affiche uniquement les caractères trouvés

-P: le pattern est une expression régulière perl.



Quantifiers & Alternation

Les quantificateurs sont des symboles qui permettent de spécifier combien de fois un élément doit apparaître dans une expression régulière. Les quantificateurs les plus couramment utilisés sont les suivants :

  • * : correspond à zéro ou plusieurs occurrences de l’élément précédent.
  • + : correspond à une ou plusieurs occurrences de l’élément précédent.
  • ? : correspond à zéro ou une occurrence de l’élément précédent.
  • {n} : correspond à exactement n occurrences de l’élément précédent.
  • {n,} : correspond à au moins n occurrences de l’élément précédent.
  • {n,m} : correspond à entre n et m occurrences de l’élément précédent.

L’alternance est un autre concept important en expressions régulières qui permet de spécifier plusieurs alternatives pour un même élément. L’alternance est spécifiée en utilisant le symbole |, qui signifie “ou”.


Voici un exemple de l’utilisation des quantificateurs et de l’alternance en Python :

1
2
3
4
5
6
import re

text = "The quick brown fox jumps over the lazy dog."
matches = re.findall("q.*?k|fox|dog", text)

print(matches)  # ['quick', 'fox', 'dog']

Dans cet exemple, l’expression régulière "q.*?k|fox|dog" recherche toutes les occurrences qui correspondent soit à "q.*?k", soit à "fox", soit à "dog". Les .*? correspondent à n’importe quel caractère qui apparaît entre les lettres "q" et "k", tandis que l’alternance | permet de spécifier les alternatives "fox" et "dog".

Voici un exemple d’utilisation des quantificateurs et de l’alternance en JavaScript :

1
2
3
4
const text = "The quick brown fox jumps over the lazy dog.";
const matches = text.match(/q.*?k|fox|dog/g);

console.log(matches);  // ['quick', 'fox', 'dog']

Dans cet exemple, l’expression régulière /q.*?k|fox|dog/g recherche toutes les occurrences qui correspondent soit à "q.*?k", soit à "fox", soit à "dog". Le .*? correspond à n’importe quel caractère qui apparaît entre les lettres "q" et "k", tandis que l’alternance | permet de spécifier les alternatives "fox" et "dog". Le flag g permet de rechercher toutes les occurrences dans le texte.

Voici un exemple d’utilisation des quantificateurs et de l’alternance en Bash :

1
2
3
4
text="The quick brown fox jumps over the lazy dog."
matches=$(echo $text | grep -oE "q.*?k|fox|dog")

echo $matches  # quick fox dog

Dans cet exemple, l’expression régulière "q.*?k|fox|dog" recherche toutes les occurrences qui correspondent soit à "q.*?k", soit à "fox", soit à "dog". Les .*? correspondent à n’importe quel caractère qui apparaît entre les lettres "q" et "k", tandis que l’alternance | permet de spécifier les alternatives "fox" et "dog".

L’option -E signifie “utiliser des expressions régulières étendues” au lieu d’utiliser des expressions régulières de base. Les expressions régulières étendues offrent des fonctionnalités plus avancées que les expressions régulières de base, telles que la possibilité d’utiliser des quantificateurs tels que +, ? et {}, ou encore l’alternance |.

L’option -o signifie “afficher uniquement les correspondances”. Cette option permet d’afficher uniquement les parties de la chaîne d’entrée qui correspondent à l’expression régulière, au lieu d’afficher toute la chaîne d’entrée avec les correspondances surlignées.



Groupes et références

Les groupes et références sont des fonctionnalités avancées des expressions régulières qui permettent de capturer des sous-parties de l’expression régulière et de les réutiliser dans la même expression régulière ou dans du code.

Un groupe est défini en entourant une partie de l’expression régulière avec des parenthèses. Les groupes peuvent être utilisés pour capturer des sous-parties de la chaîne de caractères qui correspondent à l’expression régulière. Par exemple, l’expression régulière "(\d{2})-(\d{2})-(\d{4})" capture trois groupes correspondant à la date sous la forme "jj-mm-aaaa". Chaque groupe peut ensuite être référencé en utilisant la syntaxe \N, où N est le numéro du groupe.

Voici un exemple de groupe utilisé pour capturer des adresses e-mail dans un texte :

1
([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)

Dans cet exemple, le groupe est utilisé pour capturer l’adresse e-mail complète, composée de trois parties : le nom d’utilisateur, le nom de domaine et l’extension. Le + après chaque classe de caractères signifie qu’il y a au moins un caractère qui correspond à cette classe.


Voici des exemples d’expressions régulières utilisant des groupes et références dans Python, JavaScript et Bash :

1
2
3
4
5
6
7
import re

text = "John Doe, 35 ans"
matches = re.findall("(\w+) (\w+), (\d+) ans", text)

for match in matches:
    print(f"Nom : {match[0]}, Prénom : {match[1]}, Âge : {match[2]}")
1
2
3
4
const text = "John Doe, 35 ans";
const matches = text.match(/(\w+) (\w+), (\d+) ans/);

console.log(`Nom : ${matches[2]}, Prénom : ${matches[1]}, Âge : ${matches[3]}`);
1
2
3
4
5
text='John Doe, 35 ans'
regex="([a-zA-Z]+) ([a-zA-Z]+), ([0-9]+) ans"

[[ $text =~ $regex ]]
echo "Nom: ${BASH_REMATCH[1]}, Prenom: ${BASH_REMATCH[2]}, Âge: ${BASH_REMATCH[3]}"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import re

text = "Le 01/01/2022, il s'est passé quelque chose. Le 25-12-21, il s'est également passé quelque chose. Le 30 03 2023, il va se passer quelque chose."

regex = r'(?P<day>\d{1,2})[/\- ](?P<month>\w{2})[/\- ](?P<year>\d{2,4})'

matches = re.findall(regex, text)

for match in matches:
    day, month, year = match
    print(f"La date est le {day} {month} {year}")

Explication de la regex (?P<day>\d{1,2})[/\- ](?P<month>\w{2})[/\- ](?P<year>\d{2,4}):

  • (?<day>\d{1,2}): Cela utilise la syntaxe (?<nom>pattern) pour créer un groupe nommé "day". \d{1,2} signifie qu’il y aura un ou deux chiffres.
  • [/\- ]: Correspond à l’un des caractères suivants: /, - ou un espace.
  • (?<month>\w{2}): Cela utilise la syntaxe (?<nom>pattern) pour créer un groupe nommé "month". \w{2} signifie qu’il y aura exactement deux caractères alphanumériques.
  • [/\- ]: Correspond à l’un des caractères suivants: /, - ou un espace.
  • (?<year>\d{2,4}): Cela utilise la syntaxe (?<nom>pattern) pour créer un groupe nommé "year". \d{2,4} signifie qu’il y aura deux, trois ou quatre chiffres.
1
2
3
4
5
6
7
8
const text = "Le 01/01/2022, il s'est passé quelque chose. Le 25-12-21, il s'est également passé quelque chose. Le 30 03 2023, il va se passer quelque chose.";

const regex = /(?<day>\d{1,2})[/\- ](?<month>\w{2})[/\- ](?<year>\d{2,4})/g;

let matches;
while ((matches = regex.exec(text)) !== null) {
  const { day, month, year } = matches.groups;
  console.log(`La date est le ${day} ${month} ${year}`);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
text="Le 01/01/2022, il s'est passé quelque chose. Le 25-12-21, il s'est également passé quelque chose. Le 30 03 2023, il va se passer quelque chose."
regex='([0-9]{1,2})[-/ ](\w+){2}[-/ ]([0-9]{2,4})'

echo "$text" | grep -oE "$regex" | while read -r line; do
  day=$(echo "$line" | sed -E 's/'"$regex"'/\1/')
  month=$(echo "$line" | sed -E 's/'"$regex"'/\2/')
  year=$(echo "$line" | sed -E 's/'"$regex"'/\3/')

  echo "La date est le $day $month $year"
done

Ce code extrait des dates d’un texte donné en utilisant une expression régulière pour identifier les différents formats de dates possibles (jour/mois/année), stockées dans une variable regex.

Le code utilise ensuite la commande grep pour trouver toutes les occurrences des expressions régulières correspondantes dans la variable text, puis utilise la commande sed pour extraire les différentes parties de la date (jour, mois, année) de chaque occurrence et les stocker dans des variables.

Finalement, il affiche les dates extraites dans un message.

1
2
3
La date est le 01 1 2022
La date est le 25 2 21
La date est le 30 3 2023

lookaround

Bien sûr, voici la partie sur les lookaround en incluant les lookbehind positif et négatif :

Les lookaround en expressions régulières sont des opérations de vérification de la présence d’une expression à gauche ou à droite de la position actuelle, sans faire partie de la correspondance globale. Ils permettent de spécifier des conditions pour lesquelles une expression régulière doit être valide ou non.

Il y a deux types de lookaround: les lookaheads (regard en avant) et les lookbehinds (regard en arrière). Les lookaheads permettent de spécifier des conditions à vérifier après la position actuelle, tandis que les lookbehinds permettent de spécifier des conditions à vérifier avant la position actuelle.

Les lookaheads positifs ((?=expression)) vérifient que l’expression se trouve après la position actuelle, sans inclure cette expression dans la correspondance globale. Les lookaheads négatifs ((?!expression)) vérifient le contraire : ils vérifient que l’expression ne se trouve pas après la position actuelle.


Voici un exemple d’utilisation des lookaheads en Python pour trouver tous les mots qui sont suivis par le mot “fox”:

1
2
3
4
5
6
import re

text = "The quick brown fox jumps over the lazy dog."
matches = re.findall(r'\b\w+(?= fox)', text)

print(matches)  # ['brown']

Dans cet exemple, l’expression régulière \b\w+(?= fox) recherche tous les mots (\b\w+) qui sont suivis par le mot “fox” ((?= fox)). Le \b correspond à une limite de mot. L’utilisation du lookahead permet de vérifier la présence du mot “fox” après la position actuelle, mais sans l’inclure dans la correspondance globale.

Les lookbehinds positifs ((?<=expression)) vérifient que l’expression se trouve avant la position actuelle, sans inclure cette expression dans la correspondance globale. Les lookbehinds négatifs ((?<!expression)) vérifient le contraire : ils vérifient que l’expression ne se trouve pas avant la position actuelle.

Voici un exemple d’utilisation des lookbehinds en JavaScript pour trouver tous les nombres qui sont précédés du signe “$”:

1
2
3
4
const text = "The price is $19.99 and the tax is $2.00.";
const matches = text.match(/(?<=\$)\d+\.\d{2}/g);

console.log(matches);  // ['19.99', '2.00']

Dans cet exemple, l’expression régulière /(?<=\$)\d+\.\d{2}/g recherche tous les nombres (\d+\.\d{2}) qui sont précédés du signe “$” ((?<=\$)). L’utilisation du lookbehind permet de vérifier la présence du signe “$” avant la position actuelle, mais sans l’inclure dans la correspondance globale.

Enfin, les lookbehinds ne sont pas supportés dans toutes les implémentations d’expressions régulières. Par exemple, les lookbehinds ne sont pas supportés dans les expressions régulières JavaScript avant la version ECMAScript 2018.



Flags

En plus des symboles et des concepts que nous avons déjà examinés, les expressions régulières peuvent être accompagnées de “flags” ou “options”. Les flags sont des indicateurs qui modifient la façon dont les expressions régulières fonctionnent. Les trois flags les plus couramment utilisés sont les suivants :

  • i : ce flag rend la recherche insensible à la casse. Cela signifie que la recherche correspondra à des lettres majuscules ou minuscules indifféremment. Par exemple, l’expression régulière /hello/i correspondra à “Hello”, “hello” et “hElLo”.
  • g : ce flag signifie “global” et permet de rechercher toutes les occurrences d’une correspondance dans la chaîne de texte. Par défaut, une expression régulière ne correspondra qu’à la première occurrence de la correspondance. Par exemple, l’expression régulière /hello/g correspondra à toutes les occurrences de “hello” dans le texte.
  • m : ce flag signifie “multiline” et permet à une expression régulière de correspondre à des chaînes de texte qui contiennent plusieurs lignes. Sans ce flag, l’expression régulière considérera la chaîne de texte comme une seule ligne.

Voici un exemple de l’utilisation du flag m en Python :

1
2
3
4
5
6
7
8
9
import re

text = """The quick brown fox
jumps over
the lazy dog."""

matches = re.findall("^t\w+", text, flags=re.MULTILINE)

print(matches)  # ['the']

Le flag re.MULTILINE (ou re.M) est un flag de compilation qui modifie le comportement de certaines classes de caractères et de certains quantificateurs pour qu’ils fonctionnent sur plusieurs lignes. Par exemple, sans le flag re.MULTILINE, l’expression régulière ^ ne correspond qu’au début de la chaîne. Avec le flag re.MULTILINE, l’expression régulière ^ correspond également au début de chaque ligne.

Dans cet exemple, l’expression régulière ^t\w+ recherche toutes les chaînes qui commencent par un “t” suivi de un ou plusieurs caractères de mot (\w+), en utilisant le flag re.MULTILINE. Le flag re.MULTILINE permet de rechercher la chaîne the dans toutes les lignes.

Voici un exemple d’utilisation du flag g en JavaScript :

1
2
3
4
const text = "hello world, hello universe";
const matches = text.match(/hello/g);

console.log(matches);  // ['hello', 'hello']

Dans cet exemple, le flag g est utilisé pour rechercher toutes les occurrences de la correspondance “hello” dans le texte.

Voici un exemple d’utilisation du flag i en Bash :

1
2
3
4
text="The quick brown fox jumps over the lazy dog."
matches=$(echo $text | grep -oi "FOX")

echo $matches  # FOX

Dans cet exemple, la commande grep recherche toutes les occurrences de "FOX" dans la chaîne $text, en ignorant la distinction entre majuscules et minuscules grâce au flag -i. Le flag -o permet d’afficher uniquement les parties de la chaîne d’entrée qui correspondent à l’expression régulière, au lieu d’afficher toute la chaîne d’entrée avec les correspondances surlignées.