Java Regex-Gruppen und Captures
Teile eines Regex-Treffers in Java mit Klammern, nummerierten und benannten Gruppen erfassen und auslesen.
Ein regulärer Ausdruck sagt nicht nur, ob ein String übereinstimmt – er kann den Treffer in Teile zerlegen, die man auslesen kann. Diese Teile heißen Gruppen. Indem man einen Teil eines Musters in Klammern einschließt, erzeugt man eine Capturing-Gruppe. Sobald ein Matcher erfolgreich ist, lässt sich jede Gruppe per Nummer oder Name abrufen. Dieses Kapitel behandelt nummerierte Gruppen, benannte Gruppen, Backreferences, Non-Capturing-Gruppen und wie all das in Ersetzungen einfließt.
Nummerierte Capturing-Gruppen
Jedes Klammernpaar in einem Muster öffnet eine Capturing-Gruppe, nummeriert von links nach rechts anhand ihrer öffnenden (. Gruppe 0 ist besonders: Sie ist immer der gesamte Treffer. (\d{4})-(\d{2})-(\d{2}) liefert also vier Gruppen – das komplette Datum sowie Jahr, Monat und Tag.
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() gibt die Anzahl der Capturing-Gruppen ohne Gruppe 0 zurück, sodass das obige Muster 3 meldet. Der Zugriff auf einen nicht vorhandenen Gruppenindex löst eine IndexOutOfBoundsException aus.
Benannte Gruppen
Das Zählen von Klammern wird fehleranfällig, wenn Muster wachsen. Benannte Gruppen, geschrieben als (?<name>...), ermöglichen das Auslesen eines Captures über ein lesbares Label statt einen Index. Die Namen müssen gültige Java-Bezeichner und innerhalb des Musters eindeutig sein.
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
}Benannte Gruppen sind intern nach wie vor nummeriert, sodass m.group(1) und m.group("user") denselben Text zurückgeben. Der Name dient ausschließlich der Lesbarkeit.
Backreferences
Eine Backreference stimmt mit demselben Text überein, den eine frühere Gruppe bereits erfasst hat. Im Muster schreibt man \1 für Gruppe 1 (oder \k<name> für eine benannte Gruppe). So erkennt man Wiederholungen – etwa ein doppeltes Wort – innerhalb eines einzelnen Treffers.
// \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
}Beachte den doppelten Backslash im Java-Quellcode: \\1 im String wird zu \1 im eigentlichen Regex. Eine Backreference kann erst nach dem Capture ihrer Gruppe übereinstimmen, daher muss die Gruppe früher im Muster stehen.
Capturing in Ersetzungen
String.replaceAll, Matcher.replaceAll und appendReplacement verstehen alle Gruppenreferenzen im Ersetzungstext. Verwende $1, $2, ... für nummerierte Gruppen und ${name} für benannte. Das macht Regex zu einem kleinen Umordnungs- und Templating-Werkzeug.
| Referenz | Bedeutung in der Ersetzung |
|---|---|
$0 | Der gesamte Treffer |
$1, $2, ... | Nummerierte Capturing-Gruppen |
${name} | Eine benannte Capturing-Gruppe |
\$ | Ein wörtliches Dollarzeichen |
// Reorder "First Last" into "Last, First"
String out = "Ada Lovelace".replaceAll("(\\w+)\\s+(\\w+)", "$2, $1");
System.out.println(out); // Lovelace, AdaWenn ein wörtliches $ oder \ in der Ausgabe benötigt wird, muss es als \\$ bzw. \\\\ im Java-String escaped werden.
Non-Capturing-Gruppen
Manchmal braucht man Klammern nur, um eine Alternation zu gruppieren oder einen Quantifier anzuwenden – nicht um etwas zu erfassen. Eine Non-Capturing-Gruppe (?:...) macht genau das: Sie gruppiert, ohne eine Gruppennummer zu verbrauchen, was die Indizes sauber hält und die Engine leicht schneller macht.
// 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)
}Da (?:https?|ftp) non-capturing ist, ist der Host Gruppe 1 statt Gruppe 2. Eine optionale Capturing-Gruppe, die nicht am Treffer beteiligt ist, gibt null zurück – deshalb sollte man optionale Gruppen immer auf null prüfen, bevor man sie verwendet.
Ein lauffähiges Beispiel
Das folgende Programm demonstriert jeden Gruppentyp in einem Durchlauf: nummerierte Datumsteile, benannte E-Mail-Felder, eine Backreference auf ein doppeltes Wort, Gruppenreferenzen in zwei Ersetzungen, eine Non-Capturing-Protokollgruppe und eine optionale Gruppe, die null zurückliefert.
Was aus dem Durchlauf zu entnehmen ist:
- Die beiden Datumszeilen zeigen nummerierte Gruppen:
group(0)ist der gesamte Treffer, währendgroup(1..3)Jahr, Monat und Tag nach Position erfassen. - Die E-Mail-Zeile beweist, dass benannte Gruppen denselben Text wie Indizes liefern, und
groupCount=2zählt nur die benannten Captures, nie Gruppe 0. Doubled: theundDoubled: satstammen von der\1-Backreference, die das übereinstimmt, was die Wortgruppe gerade erfasst hat – jedes wiederholte Wort wird unabhängig gefunden.Swapped: Lovelace, Ada, Turing, Alanzeigt, wie$2, $1jedes Namenspaar umordnet, währendMasked: card [4111] [2222]zeigt, wie die${num}-Referenz die Ausgabe als Template nutzt.- Das Non-Capturing-
(?:https?|ftp)hält den Host bei Gruppe 1 (w3docs.com), und die optionale Dezimalgruppe gibtfrac=nullaus, weil sie beim Abgleich von42nie beteiligt war.
Wie es weitergeht
Gruppen bauen auf dem Rest der Regex-Engine auf, daher ist es hilfreich, die umgebenden Teile zu kennen:
- Pattern und Matcher — die Klassen, deren
group()-,groupCount()- undreplaceAll()-Methoden hier aufgerufen werden. - Regex-Quantifikatoren —
{4},+und?bestimmen, wie viel jede Gruppe erfasst. - Regex-Zeichenklassen —
\d,\wund\Ssind das, woraus die meisten Gruppen bestehen. - Regex-Flags — Flags wie
CASE_INSENSITIVEändern, womit deine Gruppen übereinstimmen.