Groupes et captures dans les regex Java
Capturez des parties de texte en Java avec les parenthèses, les groupes numérotés et les groupes nommés dans les expressions régulières.
Une expression régulière ne se contente pas de vous dire si une chaîne correspond — elle peut découper la correspondance en morceaux que vous pouvez relire. Ces morceaux sont des groupes. En encadrant une partie du motif dans des parenthèses, vous créez un groupe capturant, et une fois qu'un Matcher réussit, vous extrayez chaque groupe par numéro ou par nom. Ce chapitre couvre les groupes numérotés, les groupes nommés, les rétéférences, les groupes non capturants, et comment tout cela s'intègre dans les remplacements.
Groupes capturants numérotés
Chaque paire de parenthèses dans un motif ouvre un groupe capturant, numéroté de gauche à droite par leur ( d'ouverture. Le groupe 0 est spécial : c'est toujours la correspondance entière. Ainsi, (\d{4})-(\d{2})-(\d{2}) vous donne quatre groupes — la date complète plus l'année, le mois et le jour.
Pattern p = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})");
Matcher m = p.matcher("2026-05-30");
if (m.matches()) {
System.out.println(m.group(0)); // 2026-05-30 (entire match)
System.out.println(m.group(1)); // 2026
System.out.println(m.group(2)); // 05
System.out.println(m.group(3)); // 30
}groupCount() retourne le nombre de groupes capturants sans compter le groupe 0, donc le motif ci-dessus rapporte 3. La lecture d'un indice de groupe inexistant lève une IndexOutOfBoundsException.
Groupes nommés
Compter les parenthèses devient fragile à mesure que les motifs grandissent. Les groupes nommés, écrits (?<name>...), permettent de lire une capture par un libellé lisible plutôt que par un indice. Les noms doivent être des identifiants Java valides et uniques dans le motif.
Pattern p = Pattern.compile("(?<user>[\\w.]+)@(?<host>[\\w.]+)");
Matcher m = p.matcher("[email protected]");
if (m.matches()) {
System.out.println(m.group("user")); // ada
System.out.println(m.group("host")); // math.org
}Les groupes nommés sont toujours numérotés en dessous, donc m.group(1) et m.group("user") retournent le même texte. Le nom est purement pour votre lisibilité.
Rétéférences
Une rétéférence correspond au même texte qu'un groupe précédent a déjà capturé. À l'intérieur du motif, vous écrivez \1 pour le groupe 1 (ou \k<name> pour un groupe nommé). C'est ainsi que vous détectez une répétition — par exemple, un mot doublé — au sein d'une seule correspondance.
// \b(\w+)\s+\1\b matches a word followed by the same word again
Pattern p = Pattern.compile("\\b(\\w+)\\s+\\1\\b");
Matcher m = p.matcher("the the end");
if (m.find()) {
System.out.println(m.group(1)); // the
}Notez le double antislash dans le source Java : \\1 dans la chaîne devient \1 dans la regex réelle. Une rétéférence ne peut correspondre qu'après que son groupe a capturé, donc le groupe doit apparaître plus tôt dans le motif.
Captures dans les remplacements
String.replaceAll, Matcher.replaceAll et appendReplacement comprennent tous les références de groupe dans le texte de remplacement. Utilisez $1, $2, ... pour les groupes numérotés et ${name} pour les groupes nommés. Cela transforme les regex en un petit outil de réordonnancement et de création de modèles.
| Référence | Signification dans le remplacement |
|---|---|
$0 | La correspondance entière |
$1, $2, ... | Groupes capturants numérotés |
${name} | Un groupe capturant nommé |
\$ | Un signe dollar littéral |
// Reorder "First Last" into "Last, First"
String out = "Ada Lovelace".replaceAll("(\\w+)\\s+(\\w+)", "$2, $1");
System.out.println(out); // Lovelace, AdaSi vous avez besoin d'un $ ou \ littéral dans la sortie, échappez-le avec \\$ ou \\\\ dans la chaîne Java.
Groupes non capturants
Parfois, vous avez besoin de parenthèses uniquement pour grouper une alternance ou appliquer un quantificateur — sans capturer. Un groupe non capturant (?:...) fait exactement cela : il groupe sans consommer de numéro de groupe, ce qui maintient vos indices propres et rend le moteur légèrement plus rapide.
// Group the protocol alternation, but capture only the host
Pattern p = Pattern.compile("(?:https?|ftp)://(\\S+)");
Matcher m = p.matcher("https://w3docs.com");
if (m.find()) {
System.out.println(m.group(1)); // w3docs.com (group 1, not 2)
}Comme (?:https?|ftp) est non capturant, l'hôte est le groupe 1 plutôt que le groupe 2. Un groupe capturant optionnel qui ne participe pas à la correspondance retourne null, donc vérifiez toujours la nullité des groupes optionnels avant de les utiliser.
Un exemple exécutable
Le programme ci-dessous utilise chaque type de groupe en une seule exécution : des parties de date numérotées, des champs d'email nommés, une rétéférence de mot doublé, des références de groupe dans deux remplacements, un groupe de protocole non capturant, et un groupe optionnel qui revient à null.
Ce qu'il faut retenir de l'exécution :
- Les deux lignes de date montrent les groupes numérotés :
group(0)est la correspondance entière, tandis quegroup(1..3)correspondent à l'année, au mois et au jour capturés par position. - La ligne email prouve que les groupes nommés lisent le même texte que les indices, et
groupCount=2ne compte que les captures nommées, jamais le groupe 0. Doubled: theetDoubled: satproviennent de la rétéférence\1qui correspond à ce que le groupe de mot vient de capturer — chaque mot répété est trouvé indépendamment.Swapped: Lovelace, Ada, Turing, Alanmontre$2, $1réordonnant chaque paire de noms, tandis queMasked: card [4111] [2222]montre la référence nommée${num}formatant la sortie.- Le
(?:https?|ftp)non capturant maintient l'hôte au groupe 1 (w3docs.com), et le groupe de fraction optionnel affichefrac=nullcar il n'a jamais participé à la correspondance de42.
Et ensuite
Les groupes s'appuient sur le reste du moteur regex, il est donc utile de maîtriser les éléments environnants :
- Pattern et Matcher — les classes dont les méthodes
group(),groupCount()etreplaceAll()sont appelées ici. - Quantificateurs regex —
{4},+et?décident de la quantité capturée par chaque groupe. - Classes de caractères regex —
\d,\wet\Sconstituent la plupart des groupes. - Drapeaux regex — des drapeaux comme
CASE_INSENSITIVEchangent ce que vos groupes correspondent.