Python re
Python regex with the re module: raw strings, compile, named groups, the seven flags, and the subtleties that bite.
Regex — Python
EXAMPLE
import re
# ===== Raw strings (always) =====
pattern = r'\d{4}-\d{2}-\d{2}' # raw avoids Python's own escape rules
# ===== Functions =====
re.match(pattern, '2024-04-10') # match at start
re.search(pattern, 'logged on 2024-04-10') # anywhere
re.fullmatch(pattern, '2024-04-10') # entire string
re.findall(pattern, 'a 2024-04-10 b 2024-04-11') # list of strings
re.finditer(pattern, '...') # iter of Match objects
# ===== Named groups =====
m = re.match(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})', '2024-04-10')
m.group('year') # '2024'
m.groupdict() # {'year': '2024', 'month': '04', 'day': '10'}
# ===== Compile for reuse =====
DATE = re.compile(r'^\d{4}-\d{2}-\d{2}$')
DATE.match('2024-04-10') # faster on hot paths
# ===== Substitution =====
re.sub(r'\s+', ' ', 'a b c').strip() # 'a b c'
re.sub(r'(?P<y>\d{4})-(\d{2})-(\d{2})', r'\3/\2/\g<y>', '2024-04-10')
# '10/04/2024'
# With a function:
re.sub(r'\$(\d+)', lambda m: f'AUD {int(m.group(1)):,}', 'Spent $1000 and $25000')
# ===== Flags =====
# re.IGNORECASE re.I
# re.MULTILINE re.M (^ and $ match per line)
# re.DOTALL re.S (. matches newline)
# re.VERBOSE re.X (free-spacing + comments)
# re.UNICODE re.U (default in Python 3)
# re.ASCII re.A (force \w to ASCII only)
pattern = re.compile(r'''
^
(?P<num>\d+) # the integer part
(?:\.(?P<frac>\d+))? # optional fractional part
$
''', re.VERBOSE)
# ===== Lookarounds =====
re.search(r'(?<=\$)\d+', 'price $49') # ['49']
re.findall(r'\d+(?= dollars)', '10 dollars; 5 cents') # ['10']
re.findall(r'(?<!no )spam', 'spam, no spam') # ['spam'] (only the first)
# ===== Splitting =====
re.split(r'[\s,]+', 'a b, c,d') # ['a', 'b', 'c', 'd']
# ===== Unicode classes =====
re.findall(r'\p{L}+', 'abc 中文', re.U)
# WARNING: stdlib re does not support \p{}; use the third-party 'regex' module:
# pip install regex
import regex as re2
re2.findall(r'\p{L}+', 'abc 中文')
# ===== Patterns to internalise =====
# - Raw strings for every pattern
# - re.compile in hot loops
# - Named groups when >2 captures
# - re.VERBOSE for any non-trivial pattern
# - Use the 'regex' module for Unicode property classes + advanced features
# ===== Pitfalls =====
# - Forgetting r'' -> backslashes turn into other characters
# - re.search vs re.match (match only at start)
# - greedy quantifiers across newlines without re.S
# - Catastrophic backtracking from user input (^(a+)+$)
# - \b across Unicode in stdlib re is not Unicode-aware unless re.UNICODE
Why it matters
Python regex is concise once raw strings and re.compile become reflexes. Named groups + re.VERBOSE turn long patterns into readable ones; the regex module unlocks Unicode property classes. The biggest trap is re.match (start only) when you wanted re.search (anywhere).
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
import re
m = re.search(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})', '2026-06-07')
print(m.group('year'), m.group('month'), m.group('day'))
Try it Yourself »
Discussion
Loading…