Les expressions régulières (ou regex) sont un outil puissant pour :
En Java, elles sont gérées par le package java.util.regex, avec deux classes principales :
java.util.regex
Pattern
Matcher
java.util.regex.Pattern
compile()
matcher()
split()
java.util.regex.Matcher
find()
group()
replaceAll()
matches()
PatternSyntaxException
try-catch
CharSequence
String
StringBuilder
Les classes de caractères permettent de définir des ensembles de caractères pour construire notre modèle (pattern).
\d
\d\d
\w
\w+
\s
\s+
\D
\D+
\W
\W+
\S
\S+
.
a.c
[abc]
[aeiou]
[^abc]
[^0-9]
Les quantificateurs spécifient combien de fois un élément doit apparaître dans notre modèle.
*
a*
+
a+
?
colou?r
{n}
\d{3}
{n,}
\d{2,}
{n,m}
\d{2,4}
La classe String offre des méthodes pratiques pour utiliser les regex sans créer explicitement de Pattern ou Matcher.
matches(regex)
"12/31/2023".matches("\\d{2}/\\d{2}/\\d{4}")
true
split(regex)
"1,2,3".split(",")
["1", "2", "3"]
replaceAll(regex, replacement)
replacement
"coucou".replaceAll("u", "in")
"coincoin"
replaceFirst(regex, replacement)
"coucou".replaceFirst("c", "t")
"toutou"
la notion degroup est expliquée plus loin.
import java.util.regex.*; public class DateValidateur { public static void main(String[] args) { String date = "12/30/1969"; // Regex pour MM/DD/YYYY ou MM-DD-YYYY Pattern p = Pattern.compile("^(\\d{2})[-/](\\d{2})[-/](\\d{2}(?:\\d{2})?)$"); Matcher m = p.matcher(date); // la syntaxe est monPattern.matcher(maDate) if (m.find()) { String mois = m.group(1); String jour = m.group(2); String annee = m.group(3); System.out.printf("Date valide : %s-%s-%s\n", annee, mois, jour); // version anglaise } else { System.out.println("Format de date invalide."); } } }
Explications :
^ et $
\\d{2}
[-/]
\\d{2}(?:\\d{2})?
?:
import java.util.regex.*; public class SubstitutionSimple { public static void main(String[] args) { String text = "Hello world. <br>"; // Remplacer <br> par <br /> pour la conformité XHTML (web) String resultat = text.replaceAll("<br>", "<br />"); System.out.println(resultat); // affiche "Hello world. <br />" } }
import java.util.regex.*; public class UrlToLink { public static void main(String[] args) { String text = "Visitez https://formation-java-mer.numerosoft.fr/semaine/tp/ pour plus d'infos."; String regex = "\\b(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"; Pattern p = Pattern.compile(regex); Matcher m = p.matcher(text); String resultat = m.replaceAll("<a href=\"$0\">$0</a>"); System.out.println(resultat); // Affiche: Visitez <a href="https://formation-java-mer.numerosoft.fr/semaine/tp/">https://formation-java-mer.numerosoft.fr/semaine/tp/</a> pour plus d'infos. } }
\\b
(https?|ftp|file)
://
[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*
$0
<a>
import java.util.regex.*; public class ExtracteurCourriel { public static void main(String[] args) { String text = "Contactez-nous à support@ca.com ou ventes@ca.org."; String regex = "\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}\\b"; Pattern p = Pattern.compile(regex); Matcher m = p.matcher(text); while (m.find()) { System.out.println("Courriel trouvé : " + m.group()); } } }
Résultat :
Email trouvé : support@ca.com Email trouvé : ventes@ca.org
On pourrait aller plus loin avec les groups capturants : ( … )
Un group devient intéressant quand on ajoute des parenthèses dans le regex.
Exemple modifié, on découpe l’email avec les parenthèse pour créer des group à l’intérieur du regex :
String regex = "([A-Za-z0-9._%+-]+)@([A-Za-z0-9.-]+)\\.([A-Za-z]{2,6})";
On a 3 groups :
groups
Code complet :
Pattern p = Pattern.compile(regex); Matcher m = p.matcher(text); while (m.find()) { System.out.println("Courriel complet : " + m.group(0)); // on met l'indice 0 pour avoir toute la chaine System.out.println("Utilisateur : " + m.group(1)); System.out.println("Domaine : " + m.group(2)); System.out.println("Extension : " + m.group(3)); System.out.println("----"); }
Résultats :
Email complet : support@ca.com User : support Domaine : ca Extension : com ---- Email complet : ventes@ca.org User : sales Domaine : ca Extension : org ----
Pour infos, on peut simplifier l’indentification des groupes depuis java 7+.
String regex = "(?<user>[A-Za-z0-9._%+-]+)@(?<domain>[A-Za-z0-9.-]+)\\.(?<ext>[A-Za-z]{2,6})";
Puis :
System.out.println(m.group("user")); System.out.println(m.group("domain")); System.out.println(m.group("ext"));
En Java, les backslashes () dans les expressions régulières doivent être doublés car ils sont aussi des caractères d’échappement dans les chaînes de caractères en Java.
\\d
"\\d"
\.
\\.
"\\."
\n
\\n
"\\n"
Exemple :
String regex = "\\d{3}-\\d{2}-\\d{4}"; // Correspond à "123-45-6789"
Si une regex est utilisée plusieurs fois, compilez-la une seule fois avec Pattern.compile() pour des performances optimales.
Pattern.compile()
private static final Pattern DATE_PATTERN = Pattern.compile("\\d{2}/\\d{2}/\\d{4}");
Pour les regex complexes, utilisez le flag Pattern.COMMENTS pour ajouter des commentaires. Ci-dessous, les commentaires sont les dièzes Jour, Mois et Année.
Pattern.COMMENTS
String regex = """ \\d{2} # Jour / \\d{2} # Mois / \\d{4} # Année """; Pattern p = Pattern.compile(regex, Pattern.COMMENTS);
Toujours attraper PatternSyntaxException pour les regex invalides.
try { Pattern p = Pattern.compile("[a-z"); } catch (PatternSyntaxException e) { System.err.println("Erreur dans la regex: " + e.getMessage()); }
Les regex peuvent devenir illisibles. Pour des parsings complexes, envisagez un parseur dédié (JSON avec Jackson).
Énoncé : Écrivez une regex pour valider un numéro de téléphone français (format : +33 1 23 45 67 89 ou 01 23 45 67 89).
Solution possible :
import java.util.regex.*; public class ValiderNumeroTelephone { public static void main(String[] args) { String tel = "+33 7 23 45 67 89"; String regex = "^(\\+33[ \\-]?|0)[67]([ \\-]?\\d{2}){4}$"; System.out.println(tel.matches(regex)); // true } }
^
$
(\\+33|0)
[1-9]
([ \\-]?\\d{2}){4}