| layout | default |
|---|---|
| title | Regular Expressions |
| parent | Lessons |
| nav_order | 10 |
| permalink | /lessons/regular-expressions/ |
| course_lesson | true |
| course_index | 10 |
| previous_page | /lessons/errors-logging-testing/ |
| previous_title | Errors, Logging, and Testing |
| next_page | /lessons/apis-http/ |
| next_title | APIs and HTTP |
Regular expressions, often called regex, describe text patterns. Use them when normal string methods are not enough, and keep patterns readable.
A regex is like a reusable stencil placed over text. The stencil describes shapes such as “three digits, a hyphen, then four digits.” It does not understand meaning; it only checks character patterns.
Before using regex, ask whether in, .startswith(), .endswith(), .split(), or .replace() would be clearer.
import re
match = re.search(r"\d+", "Order 123")
if match:
print(match.group())re.search() looks anywhere in the text and returns a match object or None. match.group() returns the matched text.
The pattern r"\d+" means:
\d— one digit;+— repeat the previous piece one or more times.
Use a raw string such as r"\d+" for patterns so Python does not process backslashes before the regex engine sees them.
| Pattern | Meaning |
|---|---|
. |
almost any one character |
\d |
digit |
\s |
whitespace |
\w |
Unicode word character |
[abc] |
one listed character |
[^abc] |
one character not listed |
* |
zero or more |
+ |
one or more |
? |
zero or one; also makes a quantifier lazy |
{2,4} |
between two and four repeats |
^ |
start of text (or line in multiline mode) |
$ |
end of text (or line in multiline mode) |
\A / \Z |
absolute start / end of the string |
To match a symbol that has special meaning, escape it. For example, r"\." matches a literal full stop.
text = "Student ID: ST-204"
pattern = r"ST-\d{3}"
print(re.search(pattern, text))
print(re.match(pattern, text))
print(re.fullmatch(pattern, "ST-204"))search()scans for the first match anywhere;match()checks only at the beginning;fullmatch()requires the entire string to match.
Use fullmatch() for a format validator. Use search() for finding something inside larger text.
text = "red, green; blue"
colors = re.split(r"\s*[,;]\s*", text)
numbers = re.findall(r"\d+", "A12 B7 C305")
cleaned = re.sub(r"\s+", " ", "too many\tspaces").strip()
print(colors)
print(numbers)
print(cleaned)findall() returns strings when there are no capturing groups. Capturing groups can change its result shape, so choose groups deliberately.
Parentheses group pattern pieces and capture text:
pattern = re.compile(r"(?P<level>INFO|WARNING|ERROR): (?P<message>.+)")
match = pattern.fullmatch("ERROR: Disk full")
if match:
print(match.group("level"))
print(match.group("message"))
print(match.groupdict())Use (?:...) for a non-capturing group when grouping is needed but the text does not need a numbered capture.
text = "<b>one</b><b>two</b>"
print(re.findall(r"<b>.*</b>", text))
print(re.findall(r"<b>.*?</b>", text))* and + are greedy: they consume as much as possible while allowing the pattern to finish. Adding ? makes them lazy. For real HTML, use an HTML parser rather than regex.
pattern = re.compile(
r"""
^
(?P<code>[A-Z]{2})
-
(?P<number>\d{4})
$
""",
re.VERBOSE,
)re.VERBOSE permits whitespace and comments in a complex pattern. Other common flags include re.IGNORECASE and re.MULTILINE.
pattern = "\bcat\b"valid = re.search(r"\d{4}", "code 1234 invalid")match = re.search(r"\d+", "no number")
print(match.group())Show Bug Hunter fixes
- Use a raw string:
r"\bcat\b". In an ordinary string,\bbecomes a backspace character. - Use
re.fullmatch(r"\d{4}", text)when the whole value must follow the format. - Check
if match:before accessing groups.
- Find the first number in text.
- Find all numbers.
- Validate a simple phone number.
- Extract email-like values.
- Split on commas or semicolons.
- Replace repeated whitespace.
- Match a string from the beginning and end.
- Use named groups.
- Explain a pattern in plain English.
- Build a log-line parser.
Show hints
- Use
re.search. - Use
re.findall. - Define the accepted format before writing the pattern.
- Keep validation rules realistic; regex is not full email validation.
- Use a character class such as
[;,]. - Match
\s+. - Use
^and$. - Use
(?P<name>...). - Describe each symbol before testing.
- Capture timestamp, level, and message separately.
Show solution ideas
re.search(r"\d+", text).re.findall(r"\d+", text).- Use groups for area and local number according to your chosen format.
re.findall(r"[\w.-]+@[\w.-]+", text)is only a simple extractor.re.split(r"[;,]", text).re.sub(r"\s+", " ", text).strip().re.fullmatch(pattern, text)is often clearer for full validation.- Read with
match.group("name"). - Keep a comment beside non-obvious patterns.
- Use named groups and return a dictionary.
Build a log analyzer that extracts levels and counts errors by date.
Explain the difference between search, match, full match, findall, and substitution.