regex
match extraction
programming
regular expressions
code tutorial

Extract part of a regex match

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Capturing groups in regular expressions let you extract specific parts of a match rather than the entire matched string. Parentheses () define these groups, and every major language provides access to group contents through the match object. Named groups make the extracted values self-documenting. This technique is essential for parsing log files, extracting data from structured text, and validating input formats.

Python

Use re.search() or re.match() and access groups by index or name.

python
1import re
2
3text = "Order #12345 placed on 2025-03-10"
4
5# Numbered groups
6match = re.search(r"Order #(\d+) placed on (\d{4}-\d{2}-\d{2})", text)
7if match:
8    print(match.group(1))  # "12345"
9    print(match.group(2))  # "2025-03-10"
10
11# Named groups
12match = re.search(
13    r"Order #(?P<order_id>\d+) placed on (?P<date>\d{4}-\d{2}-\d{2})", text
14)
15if match:
16    print(match.group("order_id"))  # "12345"
17    print(match.group("date"))      # "2025-03-10"
18    print(match.groupdict())        # {'order_id': '12345', 'date': '2025-03-10'}

Extracting Multiple Matches

Use re.findall() or re.finditer() to extract groups from all matches in a string.

python
1log = "Error at 10:32:05, Warning at 10:32:10, Error at 10:33:01"
2
3# findall returns list of group tuples
4times = re.findall(r"(Error|Warning) at (\d{2}:\d{2}:\d{2})", log)
5print(times)
6# [('Error', '10:32:05'), ('Warning', '10:32:10'), ('Error', '10:33:01')]
7
8# finditer returns match objects for more control
9for m in re.finditer(r"(?P<level>Error|Warning) at (?P<time>\d{2}:\d{2}:\d{2})", log):
10    print(f"{m.group('level')} -> {m.group('time')}")

JavaScript

Use String.match(), RegExp.exec(), or named groups with (?<name>...) syntax.

javascript
1const text = "Order #12345 placed on 2025-03-10";
2
3// Numbered groups
4const match = text.match(/Order #(\d+) placed on (\d{4}-\d{2}-\d{2})/);
5if (match) {
6  console.log(match[1]); // "12345"
7  console.log(match[2]); // "2025-03-10"
8}
9
10// Named groups (ES2018+)
11const named = text.match(/Order #(?<orderId>\d+) placed on (?<date>\d{4}-\d{2}-\d{2})/);
12if (named) {
13  console.log(named.groups.orderId); // "12345"
14  console.log(named.groups.date);    // "2025-03-10"
15}

All Matches with matchAll

javascript
1const log = "Error at 10:32:05, Warning at 10:32:10";
2const regex = /(?<level>Error|Warning) at (?<time>\d{2}:\d{2}:\d{2})/g;
3
4for (const m of log.matchAll(regex)) {
5  console.log(`${m.groups.level} -> ${m.groups.time}`);
6}

Go

Go uses regexp.FindStringSubmatch for numbered groups. Named groups are accessed via SubexpNames.

go
1package main
2
3import (
4    "fmt"
5    "regexp"
6)
7
8func main() {
9    re := regexp.MustCompile(`Order #(\d+) placed on (\d{4}-\d{2}-\d{2})`)
10    match := re.FindStringSubmatch("Order #12345 placed on 2025-03-10")
11
12    if match != nil {
13        fmt.Println(match[1]) // "12345"
14        fmt.Println(match[2]) // "2025-03-10"
15    }
16
17    // Named groups
18    named := regexp.MustCompile(`Order #(?P<id>\d+) placed on (?P<date>\d{4}-\d{2}-\d{2})`)
19    m := named.FindStringSubmatch("Order #12345 placed on 2025-03-10")
20    for i, name := range named.SubexpNames() {
21        if name != "" {
22            fmt.Printf("%s: %s\n", name, m[i])
23        }
24    }
25}

Non-Capturing Groups

Use (?:...) when you need grouping for alternation or repetition but do not want to capture the content.

python
1import re
2
3# Without non-capturing group: 3 groups
4m = re.search(r"(https?)(://)(example\.com)", "https://example.com")
5print(m.groups())  # ('https', '://', 'example.com')
6
7# With non-capturing group: 1 group
8m = re.search(r"(?:https?)://(.+)", "https://example.com")
9print(m.group(1))  # "example.com"

Non-capturing groups keep group indexes clean when you only need specific parts of the match.

Common Pitfalls

  • Forgetting that group index 0 is the entire match, not the first group — match.group(0) returns the full matched string, match.group(1) returns the first captured group.
  • Using re.findall() with groups and expecting full match strings — when groups are present, findall returns group tuples, not full matches.
  • Not escaping parentheses in the pattern when matching literal () characters — use \( and \) for literal parentheses.
  • Nesting groups without tracking index offsets — deeply nested groups shift the index of subsequent groups. Named groups avoid this confusion.
  • Using greedy quantifiers inside groups when lazy is needed — (.*) captures as much as possible, while (.*?) captures the minimum. This matters when multiple groups appear in the same pattern.

Summary

  • Use () to create capturing groups and extract specific parts of a regex match.
  • Access groups by index (group(1)) or by name (group("name")) with (?P<name>...) syntax.
  • Use (?:...) for non-capturing groups when you need grouping without extraction.
  • Use findall or finditer (Python), matchAll (JavaScript), or FindAllStringSubmatch (Go) for multiple matches.
  • Named groups make extracted values self-documenting and reduce index-tracking errors.

Course illustration
Course illustration

All Rights Reserved.