Regular expressions with re
Overview
The `re` module matches text against patterns. The workflow is a handful of functions plus a small pattern vocabulary. import re re.search(r'\d+', 'abc123') # first match anywhere -> Match('123') or None re.match(r'\d+', 'abc123') # match only at the START -> None here re.findall(r'\d+', 'a1 b22') # every match as a list -> ['1', '22'] re.sub(r'\s+', '_', 'a b') # replace -> 'a_b' m = re.search(r'(\d+)-(\d+)', '12-34') m.group(1), m.group(2) # '12', '34' — capture groups Core metacharacters: `\d` digit, `\w` word char, `\s` whitespace, `.` any char, `+` one-or-more, `*` zero-or-more, `?` optional, `^`/`$` start/end, `[...]` a set, `(...)` a capture group. Two rules that prevent most bugs: always write patterns as **raw strings** (`r'...'`) so backslashes survive, and remember quantifiers are **greedy** by default (`.+` grabs as much as possible — add `?` for lazy `.+?`).
Validation and extraction — parsing log lines, pulling fields out of text, checking formats like emails or IDs — are everyday tasks. Regex is interview-light but genuinely useful for text wrangling where a fixed `str` method won't do.
Where used: Validating input formats, Extracting fields from log lines / text, Search-and-replace transformations
Why learn this
- Extracting structured data from messy text and validating formats are common real-world jobs.
- Knowing raw strings and greedy-vs-lazy matching avoids the two mistakes that make regex 'mysteriously' misbehave.
Code walkthrough
import re
m = re.search(r'(\d{4})-(\d{2})-(\d{2})', 'date: 2026-07-07 ok')
print(m.group(0))
print(m.group(1), m.group(2), m.group(3))
Focus: m.group(1..3) (each parenthesized group captured separately)
Common mistakes
- Not using raw strings: Writing `'\d'` (no `r`) means Python first processes the backslash escape, which can mangle the pattern or trigger warnings. Always use `r'\d'` so the regex engine sees the backslash intact.
- Confusing match with search: `re.match` anchors at the *start* of the string; `re.search` scans anywhere. `re.match(r'\d+', 'abc1')` is None even though a digit exists — people expect search behavior and get None.
- Greedy quantifiers overshooting: `<.+>` on `'<a><b>'` matches the whole `'<a><b>'` because `.+` is greedy. Use the lazy `<.+?>` to stop at the first `>`.
Glossary
- capture group
- A parenthesized part of a pattern whose matched text can be extracted separately. Example: in `(\d+)-(\d+)`, group 1 and group 2.
- raw string
- A string literal prefixed with `r` where backslashes are literal, so `r'\d'` is backslash-d, not an escape. Always use raw strings for regex.
Recall questions
- What is the difference between `re.match` and `re.search`?
- Why should regex patterns be written as raw strings?
- What does a greedy quantifier like `.+` do, and how do you make it lazy?
Understanding checks
What does this evaluate to?
None
`re.match` only matches at the start of the string. 'abc123' begins with letters, not digits, so there's no match at position 0. `re.search` would find '123'.
What does this print?
['<a>', '<b>']
The lazy `.+?` matches as little as possible, stopping at the first `>`, so it finds two separate tags. A greedy `<.+>` would match the whole '<a><b>' as one.
Practice tasks
Extract all numbers
Using `re.findall`, extract every run of digits from the string `'order 12 has 3 items, total 450'` and print the resulting list.