Lesson 18 of 34 · 0 of 2 exercises solved
Regular Expressions
A is a tiny language for describing text patterns. Learn a dozen symbols and you can validate, extract and rewrite text that would take pages of if-statements.
The re is the 's regex engine. Write patterns as raw (r"...") so backslashes survive. re.search finds the first match anywhere; re.match only at the start; re.fullmatch requires the whole string to match.
import re
text = "Track 23, Track 150 and Track 8"
m = re.search(r"Track (\d+)", text)
print(m.group(0), m.group(1), m.span())
print(re.findall(r"\d+", text)) # every number
print(re.fullmatch(r"[A-Z][a-z]+", "Maya") is not None)
print(re.fullmatch(r"[A-Z][a-z]+", "maya") is not None)The core vocabulary: . any character, \d digit, \w word character, \s whitespace, [abc] a , [^abc] a negated set, ^ and $ anchors, and the quantifiers * (0+), + (1+), ? (0 or 1) and {m,n}. Parentheses capture; (?:...) groups without capturing.
import re
log = """
2026-09-05 08:12:01 INFO backup started
2026-09-05 08:40:17 WARN disk nearly full
2026-09-05 12:00:00 ERROR server not responding
"""
pattern = re.compile(r"^(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}):\d{2} (\w+)\s+(.*)$", re.MULTILINE)
for date, time, level, msg in pattern.findall(log):
print(f"{time} [{level}] {msg}")
# Named groups read better in real code
email = re.compile(r"(?P<user>[\w.]+)@(?P<domain>[\w.]+)")
m = email.search("write to sam.carter@example.com today")
print(m["user"], m["domain"])re.sub replaces matches, and the replacement can refer to groups with \1 or \g<name>, or be a that receives each match. re.split splits on a pattern instead of a fixed string.
import re
print(re.sub(r"\s+", " ", "too many spaces"))
print(re.sub(r"(\w+)@(\w+)", r"\2 at \1", "sam@home"))
print(re.sub(r"\d+", lambda m: str(int(m.group()) * 2), "3 apples, 5 pears"))
print(re.split(r"[,;]\s*", "red, green;blue, yellow"))Common question
Should I use regular expressions for all text matching?
A regular expression is a compact language for describing a set of strings: literal characters, character classes, anchors, quantifiers and groups. The re module uses it to search, match, extract and substitute. For what it is good at (validating formats, pulling fields out of lines, rewriting patterns) nothing is more concise.
Use raw strings so backslashes survive, compile patterns you reuse, prefer named groups for readability, and test each pattern against examples that should and should not match. Keep patterns short. When one grows past a line, split the problem up or write a small parser instead.
Source: Jamie Zawinski: "Some people, when confronted with a problem, think 'I know, I'll use regular expressions.' Now they have two problems."
Common mix-ups
Each of these sounds reasonable. Think about why it is wrong, then open the explanation.
A regex can describe any pattern, so it's the universal tool for text.
A simple check like "does this string end with .txt" needs a regex.
.* matches the shortest possible run of characters.
Practice
Write extract_dates(text) that returns every date written as YYYY-MM-DD in the text, in order, as a of strings.
Find the dates
import re
def extract_dates(text):
...
print(extract_dates("Opened 1998-03-21, renovated 2015-10-14, reopened 2026-09-05."))
Press Run to try your code, then Check answer to grade it.Write redact(text) that replaces every email address with [redacted], and mask_card(number) that keeps only the last four digits of a card number, replacing the rest with *. "4111 2222 3333 4444" becomes "**** **** **** 4444".
Substitute with care
import re
def redact(text):
...
def mask_card(number):
...
print(redact("mail sam@example.com or maya@shop.co.uk"))
print(mask_card("4111 2222 3333 4444"))
Press Run to try your code, then Check answer to grade it.Check yourself
What does the pattern r"colou?r" match?
Tip: use the ← and → keys to move between lessons.