Skip to content
intermediate Phase 14 · Python File & Data Processing

Regular Expressions

Match and extract text patterns with the re module.

1h
0 problems
Topic Progress 0%

Choosing the Right Match API

Choosing the Right Match API

Each function answers a different question:

Call Question
re.search(r"\d+", s) does any substring match?
re.match(r"\d+", s) does it match at the start?
re.fullmatch(r"\d+", s) does the whole string match?
re.findall(r"(\w+)@(\w+)", s) all matches (groups only, when groups exist)
re.split(r"[,;]", s) split keeping delimiters if they are captured
re.sub(r"\s+", " ", s) normalize replacements

Write patterns as raw strings — r"\d{3}-\d{4}" — so backslashes reach the engine instead of becoming Python escapes. re.match is not "starts with anchor": it never scans forward, so re.match(r"end", "send") fails while re.search succeeds.

Groups, Anchors, and Quantifiers

Groups, Anchors, and Quantifiers

import re

m = re.search(r"(?P<user>\w+)@(?P<host>[\w.]+)", "mail alice@example.com here")
m.group("user")        # 'alice' — named groups read better in code

re.sub(r"\b(\w)(\w*)", r"\2\1-AY", "python")   # groups in replacement text

Quantifier behavior matters:

  • x+ / x* are greedy (match as much as possible); x+? / x*? are lazy
  • ^\w+$ anchors start/end; \b marks a word boundary — essential for whole-word replaces
  • (?:...) groups without capturing (cheaper, cleaner when you only need grouping)
  • (?i), (?m), inline flags or flags=re.IGNORECASE | re.MULTILINE: . normally skips newlines unless re.DOTALL

Compilation, Performance, and Pitfalls

Compilation, Performance, and Pitfalls

Module-level calls cache up to 512 compiled patterns, but hot loops should be explicit:

VALID = re.compile(r"^[A-Z]{2}-\d{4}$")   # compile once, reuse everywhere
if VALID.fullmatch(code): ...

Catastrophic backtracking is the classic interview trap: (a+)+$ against "aaaaaaaaaaaaaaaaX" explores exponentially many partitions. Fix it by removing nested quantifiers, anchoring precisely, or — on Python 3.11+ — using possessive quantifiers a++ and atomic groups (?>...) that forbid backtracking.

Other gotchas: \d matches Unicode digits (narrow with [0-9] if you need ASCII), splitting on zero-width matches yields empty strings, and re.escape is mandatory when interpolating user text into a pattern.

Quiz

1. re.match(r"bc", "abcd") returns what?

Question 1 options

2. Why can (a+)+$ blow up on long non-matching input?

Question 2 options

3. Which pattern is lazy?

Question 3 options

Flashcards

Question

match vs search vs fullmatch

Answer

match = anchored at index 0; search = anywhere; fullmatch = entire string.

Question

Greedy vs lazy

Answer

+/* are greedy (max match); +?/*? are lazy (min match until the rest can match).

Question

re.compile use

Answer

Compile patterns used repeatedly: pattern.match(s) reuses the compiled object.

Question

Catastrophic backtracking

Answer

Nested quantifiers like (a+)+$ — fix by flattening, anchoring, or possessive/atomic constructs (3.11+).

Revision Notes

Key Takeaways

  • 1. Pick the API by the question: fullmatch for validation, search for discovery, findall/sub for transforms
  • 2. Raw strings, named groups, and explicit flags keep patterns readable
  • 3. Compile once and design patterns to avoid exponential backtracking

Interview Tips

  • • When asked to validate input, reach for fullmatch and say why partial matches are dangerous
  • • Name the failure mode of nested quantifiers and sketch the flattened rewrite

Cheat Sheet

search/anywhere, match/index0, fullmatch/whole; r"" raw; re.compile reuse; greedy vs lazy (a* vs a*?); anchors ^ $ \b; avoid (a+)+$; possessive ++ in 3.11+.

Continue Learning