Regex Groups and Named Groups

Naveen AnamaneniUpdated · 4 min read
mediumJava 25javaregex

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

Groups are numbered by their opening parenthesis; group 0 is the whole match
Dates.java▶ Try it
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() + ")");
        }
    }
}
Output
day 12, month 11, year 2026 (whole: 12-11-2026)
day 05, month 01, year 2027 (whole: 05-01-2027)

How it works

  1. Each ( ... ) is a group, numbered 1, 2, 3 in the order their opening brackets appear.
  2. group(1) returns what group 1 matched in the current match: the day.
  3. group() (or group(0)) is the whole match.

Named Groups

Numbers become confusing in long patterns. Named groups, written (?<name> ... ), let you ask for parts by name:

Named.java▶ Try it
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}"));
    }
}
Output
2026/11/12
2026/11/12
2026/11/12

How it works

  1. m.group("year") reads a named group; the code says what it means.
  2. In a replacement, $1, $2, $3 insert numbered groups, so the date's parts can be rearranged.
  3. 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:

MoreGroups.java▶ Try it
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));
        }
    }
}
Output
Advitha, groups: 1
this is a test
unit: null

How it works

  1. (?: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.
  2. \1 inside a pattern is a back-reference: "the same text that group 1 matched". (\w+) \1 finds a word followed by itself, a repeated word, which the replacement fixes.
  3. An optional group that didn't take part in the match returns null.
SyntaxMeaning
( ... )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

  1. Easy: From "Navika Anamaneni", use two groups to capture the first name and surname, and print them as "Anamaneni, Navika" with replaceAll.
  2. Harder: Parse "Rice x3 @ 54.50" with named groups item, qty and price, and print the line total.

Quick check: Groups

4 questions
1In (\d{2})-(\d{2})-(\d{4}), which group is the year?
2What is group(0)?
3How do you insert group 2 in a replacement?
4What does (?: ... ) do?

Key Takeaways

Was this tutorial helpful?

Lesson 211 of 482