Regular Expressions You Actually Use: A Practical Field Guide
The 20% of regex that solves 90% of problems — anchors, classes, quantifiers, groups and lookarounds — plus the catastrophic-backtracking trap and when not to use regex at all.
The building blocks worth memorizing
- Anchors
^$— start and end of input (or of each line with themflag). Forgetting them is the #1 reason "validation" accepts garbage:\d{4}matches "abc2026xyz". - Classes
\d\w\sand their negations\D\W\S; custom sets[A-Fa-f0-9];.matches anything but newline. - Quantifiers
*(0+),+(1+),?(0–1),{n,m}; append?to make them lazy (.*?) so they stop at the first possible match. - Groups
( )capture,(?: )group without capturing,(?<name> )names the capture so code readsmatch.groups.yearinstead ofmatch[3]. - Alternation
cat|dog— lower precedence than everything, so^cat|dog$means "starts with cat OR ends with dog"; wrap it:^(?:cat|dog)$.
Lookarounds: matching context without consuming it
Lookahead (?=…) and lookbehind (?<=…) (plus their negatives (?!…) (?<!…)) assert that something follows or precedes without including it in the match. They make otherwise-awkward jobs trivial: \d+(?= USD) grabs the number but not the currency; (?<!\$)\b\d+ finds numbers not preceded by a dollar sign; ^(?=.*[A-Z])(?=.*\d).{8,}$ is the classic password-policy check (at least one uppercase, one digit, eight characters).
Catastrophic backtracking — the regex that hangs your server
Nested quantifiers over overlapping patterns — (a+)+$, (\w+\s?)*$, (.*a){20} — can take exponential time on inputs that *almost* match. A 30-character string can keep the engine busy for minutes, and attackers know it (ReDoS). Rules: never nest unbounded quantifiers; prefer specific classes over .*; anchor early; and for user-supplied text, set a length limit before matching. Modern engines with linear-time guarantees (RE2, Rust’s regex) exist precisely because this is so easy to get wrong.
When not to use regex
- HTML, XML, JSON — nested structures are not regular languages; use a parser. Regex will work on the sample and fail on production.
- Email addresses — the fully correct pattern is thousands of characters; check for
@and a dot, then send a verification email. - Dates —
\d{2}/\d{2}/\d{4}happily accepts 99/99/9999. Parse with a date library. - Anything you cannot read back next month — if it needs a comment longer than the pattern, split it into named groups with the
x(verbose) flag or into code.
A regex is a scalpel: unmatched for slicing text by pattern, wrong for structural surgery. Knowing which job is which is most of the skill.