Regex Groups and Named Groups
Finding a date like 12-11-2026 is useful, but usually you want its parts: the day, the month and the year, separately. Wrapping parts of a pattern in parentheses creates groups, and the matcher remembers what each group matched. You can then read those parts by number or by name, or rearrange them in a replacement, for example to turn 12-11-2026 into 2026/11/12.
Numbered Groups
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Dates {
public static void main(String[] args) {
Pattern date = Pattern.compile("(\\d{2})-(\\d{2})-(\\d{4})");
Matcher m = date.matcher("Joined on 12-11-2026, renewed 05-01-2027");
while (m.find()) {
System.out.println("day " + m.group(1) + ", month " + m.group(2)
+ ", year " + m.group(3) + " (whole: " + m.group() + ")");
}
}
}day 12, month 11, year 2026 (whole: 12-11-2026)
day 05, month 01, year 2027 (whole: 05-01-2027)How it works
- Each
( ... )is a group, numbered 1, 2, 3 in the order their opening brackets appear. group(1)returns what group 1 matched in the current match: the day.group()(orgroup(0)) is the whole match.
Named Groups
Numbers become confusing in long patterns. Named groups, written (?<name> ... ), let you ask for parts by name:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Named {
public static void main(String[] args) {
Pattern date = Pattern.compile(
"(?<day>\\d{2})-(?<month>\\d{2})-(?<year>\\d{4})");
Matcher m = date.matcher("12-11-2026");
if (m.matches()) {
System.out.println(m.group("year") + "/" + m.group("month")
+ "/" + m.group("day"));
}
System.out.println("12-11-2026".replaceAll(
"(\\d{2})-(\\d{2})-(\\d{4})", "$3/$2/$1"));
System.out.println("12-11-2026".replaceAll(
"(?<d>\\d{2})-(?<m>\\d{2})-(?<y>\\d{4})", "${y}/${m}/${d}"));
}
}2026/11/12
2026/11/12
2026/11/12How it works
m.group("year")reads a named group; the code says what it means.- In a replacement,
$1,$2,$3insert numbered groups, so the date's parts can be rearranged. - Named groups are inserted with
${name}.
Non-Capturing Groups and Back-References
Sometimes you need brackets only to group alternatives, not to capture them. (?: ... ) groups without capturing, so it doesn't use up a number:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MoreGroups {
public static void main(String[] args) {
Matcher title = Pattern.compile("(?:Mr|Ms|Dr)\\. (\\w+)")
.matcher("Dr. Advitha");
if (title.matches()) {
System.out.println(title.group(1)
+ ", groups: " + title.groupCount());
}
String typo = "this is is a test";
System.out.println(typo.replaceAll("\\b(\\w+) \\1\\b", "$1"));
Matcher weight = Pattern.compile("(\\d+)(kg)?").matcher("12");
if (weight.matches()) {
System.out.println("unit: " + weight.group(2));
}
}
}Advitha, groups: 1
this is a test
unit: nullHow it works
(?:Mr|Ms|Dr)groups the three titles for the|alternatives but doesn't capture, so the name is group 1, and there's only one group.\1inside a pattern is a back-reference: "the same text that group 1 matched".(\w+) \1finds a word followed by itself, a repeated word, which the replacement fixes.- An optional group that didn't take part in the match returns
null.
| Syntax | Meaning |
|---|---|
( ... ) | Capturing group, numbered |
(?<name> ... ) | Named capturing group |
(?: ... ) | Group without capturing |
\1, \k<name> | Back-reference inside the pattern |
$1, ${name} | Insert a group in a replacement |
Common Mistakes
Counting groups by closing brackets. Number them by opening brackets, left to right.
Reading group(n) before calling find() or matches(). It throws IllegalStateException: No match found.
Not checking optional groups for null. A group that didn't participate returns null.
Practice
- Easy: From
"Navika Anamaneni", use two groups to capture the first name and surname, and print them as"Anamaneni, Navika"withreplaceAll. - Harder: Parse
"Rice x3 @ 54.50"with named groupsitem,qtyandprice, and print the line total.
Key Takeaways
Was this tutorial helpful?