⚡IntermediatePython in Practice

Regular Expressions

Pattern matching and text extraction with Python's re module search, match, findall, sub.

Overview

Regular expressions (regex) are a mini-language for describing text patterns. Python's `re` module provides full regex support. They're invaluable for validating input, extracting data, and transforming strings. Key functions: - `re.search(pattern, string)` find the first match anywhere in the string - `re.match(pattern, string)` match only at the start - `re.findall(pattern, string)` return all non-overlapping matches as a list - `re.sub(pattern, replacement, string)` replace matches - `re.compile(pattern)` pre-compile a pattern for reuse Always use **raw strings** (`r"\d+"`) for patterns they prevent Python from interpreting backslashes before the regex engine sees them. Groups `(pattern)` capture parts of a match. Named groups `(?P<name>pattern)` make the code self-documenting. Lookahead `(?=...)` and lookbehind `(?<=...)` match positions without consuming characters.

Key Points

  • •Always use raw strings for patterns: `r"\d+"` not `"\\d+"`
  • •\d digit, \w word char, \s whitespace, \b word boundary, . any char (except newline)
  • •`*` 0 or more, `+` 1 or more, `?` 0 or 1, `{n,m}` between n and m
  • •Groups `()` capture; `(?:)` groups without capture; `(?P<name>)` named groups
  • •`re.IGNORECASE` (or `re.I`) flag for case-insensitive matching
  • •`match.group(0)` whole match; `match.group(1)` first capture group
  • •`re.compile()` pre-compiles patterns used repeatedly better performance

Examples

search(), match(), and findall()

import re

text = "Order #12345 placed on 2026-01-15 for $99.99"

# search first match anywhere
m = re.search(r"\d{4}-\d{2}-\d{2}", text)
if m:
    print(m.group())   # 2026-01-15

# findall all matches
numbers = re.findall(r"\d+", text)
print(numbers)         # ['12345', '2026', '01', '15', '99', '99']

# match only from the start
print(re.match(r"Order", text))     # Match object
print(re.match(r"\d+", text))      # None doesn't start with digits

Groups and substitution

import re

# Capture groups
date_str = "Today is 2026-06-15"
m = re.search(r"(\d{4})-(\d{2})-(\d{2})", date_str)
if m:
    print(m.group(0))   # 2026-06-15 (full match)
    print(m.group(1))   # 2026 (year)
    print(m.group(2))   # 06  (month)

# Named groups
m2 = re.search(r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})", date_str)
if m2:
    print(m2.group("year"))   # 2026

# sub replace
clean = re.sub(r"\s+", " ", "too   many    spaces")
print(clean)   # too many spaces

# Replace with a function
result = re.sub(r"\d+", lambda m: str(int(m.group()) * 2), "a1 b2 c3")
print(result)  # a2 b4 c6

Compiled patterns and common use cases

import re

# Compile for reuse
email_re = re.compile(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}")
phone_re = re.compile(r"\b\d{3}[-.]\d{3}[-.]\d{4}\b")

texts = [
    "Contact alice@example.com or 555-123-4567",
    "bob@test.org is available",
    "Call 800.555.1234 for support",
]
for t in texts:
    emails = email_re.findall(t)
    phones = phone_re.findall(t)
    if emails: print("Email:", emails)
    if phones: print("Phone:", phones)

Common Mistakes

  • Not using raw strings: `"\d"` is an invalid escape; `r"\d"` is correct
  • Using re.match() when you want re.search() match() only checks from the start of the string
  • Greedy vs lazy matching: `.*` is greedy (matches as much as possible); `.*?` is lazy (matches as little as possible)

Note

Test your regex patterns interactively at regex101.com it highlights matches, explains each part of the pattern, and shows edge cases. Save yourself hours of trial and error.

Exercises

Write Python code to solve each exercise. Run it in your browser and check your output.

Loading editor…

Completed: 0 / 5 exercises

Knowledge Check

Question 1 of 4

What is the difference between `re.match()` and `re.search()`?