← Back to all articles

Practical Regex Recipes and Four Common Traps

RegexPitfalls

Accept one fact first: regex is not universal

Regex excels at clear, fixed-structure matching: log lines, config fragments, format pre-checks. It is poor at nested structures — use a real parser for HTML, JSON or source code. Parsing nested content with regex fails eventually.

Frequently used recipes

NeedPatternNotes
Email (loose)[\w.+-]+@[\w-]+\.[\w.-]+Format pre-check only; verification is sending mail
Mainland China mobile1[3-9]\d{9}Prefixes change; do not hardcode every range
IPv4 (four groups)(?:\d{1,3}\.){3}\d{1,3}Does not check 0-255; validate numerically too
ISO date\d{4}-\d{2}-\d{2}No calendar validity; 2026-19-99 passes
Trim both ends^\s+|\s+$Prefer trim() in most languages
Collapse repeated lines^(.*)(?:\r?\n\1)+$Requires multiline mode

Four mechanisms you must know

  1. Greedy vs lazy: .* matches as much as possible; .*? as little as possible. This is the most common source of bugs;
  2. Groups: () captures, (?:) does not, and \1 back-references what an earlier group matched;
  3. Lookaround: (?=...) positive and (?!...) negative lookahead assert what must or must not follow;
  4. Anchors and flags: ^ and $ behave very differently with m (multiline) and s (dot matches newline).

Catastrophic backtracking (ReDoS)

Nested quantifiers like (a+)+$ make matching time grow exponentially on long non-matching input, pinning the CPU. Avoid nesting quantifiers, use possessive quantifiers or atomic groups where supported, cap the length of user input, and never let user-supplied patterns run server-side unchecked.

Practical advice

  • Write "should match" and "should not match" samples before the pattern;
  • Split hard validation into "regex pre-check + precise code check";
  • Use named groups so extraction stays readable;
  • Treat regex as code: comment it, store it as a constant, review it.

Common questions

Why did .* swallow the whole line? Greedy behaviour — use .*? or a restricted class such as [^"]*. Why does \d match full-width digits? Depends on Unicode mode in your engine; write [0-9] when you mean ASCII. Can regex check password strength? It can check length and character classes, but strength is entropy — a dedicated estimator fits better.

How to debug a pattern

Use a regex tester (or your editor's regex search) and verify in three stages: first the character class, then the quantifier, then the anchors. Testing quantifiers separately from groups shows quickly which part over-matches. Also keep "should not match" samples — testing only positive cases hides the worst problem of all, over-matching.

Related reading: Strong passwords: why length beats complexity

Keeping regex maintainable

The hard part is not writing a pattern but understanding and changing it six months later.

  1. Name and comment: define common patterns as named constants with comments describing what they accept and reject. Inlining expressions in business logic is the surest way to lose control.
  2. Split and compose: break complex patterns into testable fragments and combine them — reusable, and it isolates which piece fails.
  3. Pair with test cases: keep matching and non-matching samples per pattern, especially near boundaries such as one-digit input, very long strings and text with newlines.
  4. Prefer simpler tools: substring and prefix checks need plain string methods — faster and far more readable than a pattern.
  5. Mind language differences: implementations vary on assertions, anchors and escaping, so re-verify rather than copying across languages.

With these habits, regex stops being code only the author dares touch and becomes ordinary maintainable logic.