Expressions régulières en Java — java.util.regex
Prérequis
Maîtriser la classe String et ses méthodes de recherche. Comprendre les notions de pattern, de compilation et de correspondance. Avoir des bases sur la gestion des exceptions.
Objectifs
Comprendre les trois classes du package java.util.regex. Construire des expressions régulières avec les métacaractères Java. Utiliser Pattern, Matcher et leurs méthodes pour rechercher, valider et remplacer du texte.
Un peu d'histoire
Les expressions régulières (regex) ont été inventées par le mathématicien Stephen Kleene dans les années 1950. Elles ont été popularisées par les outils Unix (grep, sed, awk) et sont aujourd'hui disponibles dans tous les langages modernes. Java les intègre nativement via le package java.util.regex depuis Java 1.4.
1. Architecture du package java.util.regex
Le package java.util.regex repose sur trois classes principales :
| Classe | Rôle | Création |
|---|---|---|
Pattern |
Représentation compilée d'une expression régulière | Pattern.compile(regex) — pas de constructeur public |
Matcher |
Moteur qui applique le Pattern à une chaîne cible | pattern.matcher(texte) — pas de constructeur public |
PatternSyntaxException |
Exception levée si la syntaxe du Pattern est invalide | Levée automatiquement par Pattern.compile() |
import java.util.regex.Pattern;
import java.util.regex.Matcher;
String regex = "motif";
String texte = "texte à analyser";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(texte);
if (matcher.find()) {
System.out.println("Trouvé !");
}
2. Syntaxe des expressions régulières
2.1 Ancres et positions
| Expression | Correspondance | Exemple |
|---|---|---|
^ |
Début de ligne | ^Bonjour → la ligne commence par "Bonjour" |
$ |
Fin de ligne | monde$ → la ligne se termine par "monde" |
\A |
Début de la chaîne entière | Indépendant du mode multiligne |
\z |
Fin absolue de la chaîne | Aucune nouvelle ligne finale tolérée |
\b |
Frontière de mot | \bdev\b → "dev" entier, pas "developpement" |
\B |
Non-frontière de mot | \Bdev\B → "dev" au milieu d'un mot |
2.2 Classes de caractères
| Expression | Correspondance | Équivalent |
|---|---|---|
. |
N'importe quel caractère sauf saut de ligne | — |
[abc] |
Un caractère parmi a, b ou c | — |
[^abc] |
Tout caractère sauf a, b, c | — |
[a-z] |
Une lettre minuscule de a à z | — |
[A-Z0-9] |
Lettre majuscule ou chiffre | — |
\d |
Un chiffre | [0-9] |
\D |
Un non-chiffre | [^0-9] |
\w |
Un caractère alphanumérique ou _ | [a-zA-Z0-9_] |
\W |
Un caractère non alphanumérique | [^a-zA-Z0-9_] |
\s |
Un espace blanc (espace, tab, saut de ligne…) | [\t\n\r\f ] |
\S |
Un caractère non-espace | — |
2.3 Quantificateurs
| Quantificateur | Signification | Exemple |
|---|---|---|
* |
0 ou plusieurs occurrences | ab* → "a", "ab", "abb", "abbb"… |
+ |
1 ou plusieurs occurrences | ab+ → "ab", "abb", "abbb"… |
? |
0 ou 1 occurrence | colou?r → "color" ou "colour" |
{n} |
Exactement n occurrences | \d{4} → exactement 4 chiffres |
{n,} |
Au moins n occurrences | \d{2,} → 2 chiffres ou plus |
{n,m} |
Entre n et m occurrences | \d{2,4} → 2 à 4 chiffres |
2.4 Groupes et alternance
| Expression | Signification |
|---|---|
(re) |
Groupe capturant — mémorise le texte correspondant |
(?:re) |
Groupe non capturant — regroupe sans mémoriser |
a|b |
Alternance — correspond à a ou b |
2.5 Échappement des métacaractères
\ est un caractère d'échappement dans les chaînes String. Pour représenter \d dans une regex Java, il faut écrire "\\d" — la première barre échappe la seconde pour la chaîne, et la paire \\ produit un seul \ transmis au moteur regex.
String regex = "\\d+"; // regex : \d+ (un ou plusieurs chiffres)
String regex = "\\bdev\\b"; // regex : \bdev\b (mot entier "dev")
String regex = "\\.com"; // regex : \.com (point littéral + "com")
3. La classe Pattern
Pattern est la représentation compilée d'une expression régulière. La compilation est coûteuse — si vous réutilisez le même pattern, compilez-le une seule fois et réutilisez l'objet Pattern.
Pattern p1 = Pattern.compile("\\d+");
Pattern p2 = Pattern.compile("^[A-Z]", Pattern.CASE_INSENSITIVE);
Pattern p3 = Pattern.compile(
"^[a-zA-Z0-9._%+-]+" +
"@[a-zA-Z0-9.-]+" +
"\\.[a-zA-Z]{2,}$"
);
boolean valide = p3.matcher("user@example.com").matches();
System.out.println("Email valide : " + valide);
Email valide : true
4. La classe Matcher et ses méthodes
4.1 Méthodes d'étude (recherche)
| Méthode | Description | Point de départ |
|---|---|---|
find() |
Cherche la prochaine sous-séquence correspondant au motif | Après la dernière correspondance (ou 0) |
find(int start) |
Cherche à partir de l'indice start |
Position spécifiée |
matches() |
Vérifie si toute la chaîne correspond au motif | Début de la chaîne |
lookingAt() |
Vérifie si le début de la chaîne correspond au motif | Début de la chaîne |
4.2 Méthodes d'indexation
| Méthode | Description |
|---|---|
start() |
Indice de début de la dernière correspondance |
end() |
Indice (exclu) de fin de la dernière correspondance |
start(int group) |
Indice de début du groupe capturant numéro group |
end(int group) |
Indice de fin du groupe capturant numéro group |
group() |
Texte de la dernière correspondance complète (groupe 0) |
group(int n) |
Texte capturé par le groupe numéro n |
groupCount() |
Nombre de groupes capturants dans le pattern |
4.3 Méthodes de remplacement
| Méthode | Description |
|---|---|
replaceAll(String remplacement) |
Remplace toutes les occurrences correspondantes |
replaceFirst(String remplacement) |
Remplace uniquement la première occurrence |
appendReplacement(StringBuffer sb, String r) |
Remplacement incrémental — étape intermédiaire |
appendTail(StringBuffer sb) |
Ajoute le reste de la chaîne après le dernier remplacement |
5. Groupes de capture
() dans le pattern. Il mémorise la portion de texte qui correspond à la partie du motif entre ces parenthèses. Les groupes sont numérotés de gauche à droite en comptant les parenthèses ouvrantes. Le groupe 0 représente toujours la correspondance entière.Exemple 1 — Groupes de capture
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TestGroupes {
public static void main(String[] args) {
String line = "www.developpement123.com";
String pattern = "(.*)([0-9]+)(.*)";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(line);
System.out.println("Nombre de groupes : " + m.groupCount());
if (m.find()) {
System.out.println("Groupe 0 (tout) : " + m.group(0));
System.out.println("Groupe 1 (.*) : " + m.group(1));
System.out.println("Groupe 2 ([0-9]+): " + m.group(2));
System.out.println("Groupe 3 (.*) : " + m.group(3));
}
}
}
Nombre de groupes : 3 Groupe 0 (tout) : www.developpement123.com Groupe 1 (.*) : www.developpement12 Groupe 2 ([0-9]+): 3 Groupe 3 (.*) : .com
.* est glouton : il capture autant de caractères que possible. C'est pourquoi le groupe 1 capture "www.developpement12" et non "www.developpement" — il laisse le minimum possible au groupe 2. Pour un comportement non glouton (paresseux), utilisez .*?.6. Exemples pratiques
Exemple 2 — Trouver toutes les occurrences d'un motif
import java.util.regex.*;
Pattern pattern = Pattern.compile("dev");
Matcher m = pattern.matcher("info-dev : www.developpement-informatique.com");
while (m.find()) {
System.out.println("Motif trouvé de " + m.start() + " à " + (m.end() - 1));
}
Motif trouvé de 5 à 7 Motif trouvé de 15 à 17
Exemple 3 — Mots entiers avec \b
Pattern pattern = Pattern.compile("\\bdev\\b");
Matcher m = pattern.matcher("info dev : www.developpement-informatique.com");
int cpt = 0;
while (m.find()) {
cpt++;
System.out.println("Mot " + cpt + " : indice " + m.start() + " à " + (m.end() - 1));
}
System.out.println("Total : " + cpt + " occurrence(s)");
Mot 1 : indice 5 à 7 Total : 1 occurrence(s)
Exemple 4 — matches() vs lookingAt() vs find()
Pattern pattern = Pattern.compile("dev");
Matcher matcher = pattern.matcher("developpement-informatique.com");
System.out.println("find() : " + matcher.find());
matcher.reset();
System.out.println("lookingAt() : " + matcher.lookingAt());
matcher.reset();
System.out.println("matches() : " + matcher.matches());
find() : true (trouve "dev" n'importe où) lookingAt() : true (la chaîne COMMENCE par "dev") matches() : false (la chaîne entière n'est PAS "dev")
Exemple 5 — replaceAll() et replaceFirst()
Pattern pattern = Pattern.compile("dev");
Matcher matcher = pattern.matcher("info-dev, dev-info, dev");
String remplaceAll = matcher.replaceAll("soft");
System.out.println("replaceAll : " + remplaceAll);
matcher.reset();
String remplaceFirst = matcher.replaceFirst("soft");
System.out.println("replaceFirst : " + remplaceFirst);
replaceAll : info-soft, soft-info, soft replaceFirst : info-soft, dev-info, dev
Exemple 6 — appendReplacement / appendTail
Pattern pattern = Pattern.compile("dev");
Matcher matcher = pattern.matcher("info-dev, dev-info");
StringBuffer resultat = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(resultat, "soft");
}
matcher.appendTail(resultat);
System.out.println(resultat);
info-soft, soft-info
7. Gestion des erreurs — PatternSyntaxException
Pattern.compile() si la syntaxe de l'expression régulière est invalide. Ses méthodes permettent de diagnostiquer l'erreur.| Méthode | Description |
|---|---|
getDescription() |
Description textuelle de l'erreur |
getIndex() |
Position dans le pattern où l'erreur se produit |
getPattern() |
Le pattern erroné |
getMessage() |
Message complet avec description, indice et visualisation |
import java.util.regex.*;
try {
Pattern p = Pattern.compile("[chiffre invalide");
} catch (PatternSyntaxException e) {
System.out.println("Erreur dans le pattern !");
System.out.println("Description : " + e.getDescription());
System.out.println("Indice : " + e.getIndex());
System.out.println("Pattern : " + e.getPattern());
}
Erreur dans le pattern ! Description : Unclosed character class Indice : 0 Pattern : [chiffre invalide
8. Patterns de validation courants
Complément — Regex utiles en pratique
| Cas d'usage | Expression régulière |
|---|---|
| Entier positif | ^\d+$ |
| Nombre décimal | ^\d+(\.\d+)?$ |
| Adresse email (simplifiée) | ^[\w._%+-]+@[\w.-]+\.[a-zA-Z]{2,}$ |
| Numéro de téléphone marocain | ^0[5-7]\d{8}$ |
| Code postal (5 chiffres) | ^\d{5}$ |
| URL http/https (simplifiée) | ^https?://[\w.-]+\.[a-z]{2,} |
| Mot de passe (8+ car., maj., chiffre) | ^(?=.*[A-Z])(?=.*\d).{8,}$ |
| Date JJ/MM/AAAA | ^\d{2}/\d{2}/\d{4}$ |
9. Exercice
Validateur et extracteur de données
Écrire un programme Java qui effectue trois tâches distinctes avec les expressions régulières.
Travail demandé
- Validation d'email : écrire une méthode
static boolean validerEmail(String email)qui utilisematches()pour valider une adresse email avec le pattern^[\w._%+-]+@[\w.-]+\.[a-zA-Z]{2,}$. - Extraction de nombres : écrire une méthode
static void extraireNombres(String texte)qui utilisefind()en boucle pour trouver et afficher tous les nombres entiers dans une chaîne. - Nettoyage de texte : écrire une méthode
static String nettoyerEspaces(String texte)qui remplace toute séquence d'espaces multiples par un seul espace avecreplaceAll().
=== Validation emails === mostafa@um6p.ma : valide contact@.invalid : invalide user.name+tag@site.fr : valide @nodomain.com : invalide === Extraction de nombres === Texte : "Java 21 sort en 2023 avec 15 nouvelles fonctionnalités" Nombres trouvés : 21, 2023, 15 === Nettoyage d'espaces === Avant : "Java est un langage" Après : "Java est un langage"
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ValidateurRegex {
static final Pattern EMAIL_PATTERN =
Pattern.compile("^[\\w._%+-]+@[\\w.-]+\\.[a-zA-Z]{2,}$");
static final Pattern NOMBRE_PATTERN =
Pattern.compile("\\d+");
static final Pattern ESPACES_PATTERN =
Pattern.compile("\\s+");
static boolean validerEmail(String email) {
return EMAIL_PATTERN.matcher(email).matches();
}
static void extraireNombres(String texte) {
System.out.println("Texte : \"" + texte + "\"");
Matcher m = NOMBRE_PATTERN.matcher(texte);
StringBuilder nombres = new StringBuilder("Nombres trouvés : ");
boolean premier = true;
while (m.find()) {
if (!premier) nombres.append(", ");
nombres.append(m.group());
premier = false;
}
System.out.println(nombres);
}
static String nettoyerEspaces(String texte) {
return ESPACES_PATTERN.matcher(texte).replaceAll(" ");
}
public static void main(String[] args) {
System.out.println("=== Validation emails ===");
String[] emails = {
"mostafa@um6p.ma",
"contact@.invalid",
"user.name+tag@site.fr",
"@nodomain.com"
};
for (String email : emails) {
System.out.printf("%-25s : %s%n",
email,
validerEmail(email) ? "valide" : "invalide");
}
System.out.println("\n=== Extraction de nombres ===");
extraireNombres("Java 21 sort en 2023 avec 15 nouvelles fonctionnalités");
System.out.println("\n=== Nettoyage d'espaces ===");
String brut = "Java est un langage";
System.out.println("Avant : \"" + brut + "\"");
System.out.println("Après : \"" + nettoyerEspaces(brut) + "\"");
}
}
=== Validation emails === mostafa@um6p.ma : valide contact@.invalid : invalide user.name+tag@site.fr : valide @nodomain.com : invalide === Extraction de nombres === Texte : "Java 21 sort en 2023 avec 15 nouvelles fonctionnalités" Nombres trouvés : 21, 2023, 15 === Nettoyage d'espaces === Avant : "Java est un langage" Après : "Java est un langage"
- Compilation statique : les
Patternsont compilés une seule fois en constantesstatic final— meilleure performance si les méthodes sont appelées fréquemment. matches(): compare la chaîne entière au pattern — idéal pour la validation. Avec^...$, le comportement est identique àmatches()maismatches()est plus sûr car il ne nécessite pas les ancres.find()en boucle : chaque appel avance dans la chaîne et retourne la prochaine occurrence — parfait pour extraire toutes les correspondances.replaceAll("\\s+"):\\s+correspond à une ou plusieurs espaces consécutives — les remplace toutes par un seul espace.
L'essentiel en bref
Le package java.util.regex expose trois classes : Pattern (motif compilé créé via Pattern.compile()), Matcher (moteur de correspondance créé via pattern.matcher(texte)), et PatternSyntaxException (erreur de syntaxe dans le pattern). Les méthodes clés du Matcher sont find() pour rechercher la prochaine occurrence, matches() pour valider toute la chaîne, lookingAt() pour vérifier le début, replaceAll() et replaceFirst() pour substituer du texte, et group(n) pour extraire les groupes capturants. En Java, chaque \ regex s'écrit \\ dans une chaîne Java — toujours doubler les barres obliques inverses. Compilez vos Pattern une seule fois (constante static final) si vous les réutilisez fréquemment.
Discussion (0)
Soyez le premier à laisser un commentaire !
Laisser un commentaire
Votre commentaire sera visible après modération.