When not to use a regex

Regular expressions · Practical · 8 min

Knowing a tool's limits is what keeps a clever one-liner from becoming a production bug.

Regular expressions describe regular languages: patterns that need only a fixed amount of memory to recognise. They cannot count arbitrarily deep nesting, so they can't correctly match balanced parentheses, nested HTML tags or JSON objects in general. (Some engines have recursion extensions, but the result is unreadable and fragile.)

Use a real parser instead:

  • HTML and XML: an HTML parser (BeautifulSoup, lxml, the DOM).
  • JSON, YAML, TOML, CSV: the language's library for that format; CSV in particular has quoting rules a simple split gets wrong.
  • URLs: urllib.parse, new URL().
  • Dates and numbers: datetime.strptime or a date library will reject 2026-02-30, which \d{4}-\d{2}-\d{2} happily accepts.

Some validations are better done loosely. A fully RFC-compliant email regex is enormous and still can't tell whether the address exists; check for something like ^[^@\s]+@[^@\s]+$ and then send a confirmation email.

Regex shines for: searching code and logs, extracting fields from semi-structured lines, quick validation of simple formats (IDs, postcodes, version numbers), and search-and-replace. If a pattern needs comments to explain, or grows beyond a line or two, consider a small hand-written parser or splitting the job into steps with ordinary string methods.

Example

import re, json
from datetime import datetime
from urllib.parse import urlparse

print(bool(re.fullmatch(r"\d{4}-\d{2}-\d{2}", "2026-02-30")))   # True (but not a date!)
try:
    datetime.strptime("2026-02-30", "%Y-%m-%d")
except ValueError as e:
    print("rejected:", e)            # rejected: day is out of range for month

doc = '{"a": {"b": [1, {"c": 2}]}}'
print(json.loads(doc)["a"]["b"][1]["c"])                         # 2

u = urlparse("https://ex.com:8080/p/a?q=1#top")
print(u.hostname, u.port, u.query)                               # ex.com 8080 q=1

print(bool(re.fullmatch(r"[^@\s]+@[^@\s]+", "ana@example.org")))  # True: loose check

Remember

  • Regexes can't match arbitrarily nested structures
  • Use parsers for HTML, JSON, CSV, URLs and dates
  • Validate emails loosely and confirm them another way

Watch out for

  • Scraping HTML with regexes that break as soon as attribute order or whitespace changes.

Check yourself

  1. Why can't a classic regular expression check that parentheses are balanced to any depth?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/howto/regex.html#common-problems

Builds on

Unlocks

Nothing builds on this yet.