Capture groups and named groups

Regular expressions · Practical · 12 min

Pulling structured fields out of log lines, dates and URLs is the most common real use of regex.

Each pair of capturing parentheses records the text it matched. Groups are numbered by their opening parenthesis, left to right, starting at 1; group 0 is the whole match.

Named groups make patterns self-documenting and robust to edits:

  • Python: (?P<year>\d{4}), and since 3.12 also (?<year>\d{4}); read with m["year"] or m.groupdict().
  • JavaScript, PCRE, .NET, Java: (?<year>\d{4}); in JS read m.groups.year.

Details worth knowing:

  • A group inside a quantifier keeps only its last iteration: (\d)+ on "123" captures 3.
  • An optional group that didn't participate returns None in Python and undefined in JavaScript.
  • findall returns tuples of groups when there are several; finditer gives full match objects, which is usually clearer.
  • Prefer non-capturing (?:...) for groups you don't read, so numbering stays stable.

For repeated structured extraction, compile the pattern once (re.compile) and iterate with finditer. When the data is a real format (JSON, CSV, URLs, HTML), use its parser instead of groups; regex extraction is best for semi-structured text like log lines.

Example

import re

line = "2026-10-17 12:04:55 ERROR [db] connection lost"
pat = re.compile(r"(?P<date>\d{4}-\d{2}-\d{2}) \S+ (?P<level>[A-Z]+) \[(?P<src>\w+)\]")
m = pat.search(line)
print(m.group(0))          # 2026-10-17 12:04:55 ERROR [db]
print(m["level"], m[3])    # ERROR db
print(m.groupdict())       # {'date': '2026-10-17', 'level': 'ERROR', 'src': 'db'}

print(re.search(r"(\d)+", "123").group(1))       # 3  (last iteration)
print(re.search(r"(a)?b", "b").group(1))         # None

for m in re.finditer(r"(\w+)=(\d+)", "x=1 y=22"):
    print(m.group(1), int(m.group(2)))           # x 1 / y 22

Remember

  • Groups are numbered by their opening parenthesis; group 0 is the whole match
  • Named groups: (?P<name>...) in Python, (?<name>...) almost everywhere
  • A repeated group keeps only its last iteration

Watch out for

  • Expecting (\d)+ to capture every digit; capture the whole run with (\d+) instead.

Check yourself

  1. In ((a)(b)), which group captures b?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/re.html#match-objects