Regex Cookbook Cheat Sheet
A practical reference of regular expression syntax, character classes, quantifiers, groups, and common validation patterns.
Character Classes & Anchors
Building blocks for matching character types and positions.
- .- Matches any character except newline (unless dotall/s flag is set)
- \d \w \s- Digit, word character (`[A-Za-z0-9_]`), whitespace — uppercase negates each
- [abc] / [^abc]- Matches any character in the set / any character NOT in the set
- [a-z0-9]- Character range within a set
- ^ / $- Start of string (or line in multiline mode) / end of string or line
- \b / \B- Word boundary / non-word boundary
Quantifiers & Groups
Repetition and capturing syntax.
- * + ?- Zero or more, one or more, zero or one occurrences
- {n,m}- Between n and m repetitions; `{n}` exact, `{n,}` at least n
- (...)- Capturing group; can be referenced later as \1, $1, etc.
- (?:...)- Non-capturing group; groups without creating a backreference
- (?<name>...)- Named capturing group, referenced as group('name') in most engines
- *? +? ??- Lazy (non-greedy) versions of the quantifiers, matching as little as possible
Common Validation Patterns
Ready-to-use patterns for typical inputs.
^[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}$ # basic email^https?://[\w.-]+(:\d+)?(/\S*)?$ # basic URL^\d{3}-\d{3}-\d{4}$ # US phone (123-456-7890)^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$ # password: upper+lower+digit, 8+ chars^\d{4}-\d{2}-\d{2}$ # ISO date YYYY-MM-DD^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$ # hex color code
Lookaround & Flags
Zero-width assertions and common engine flags.
foo(?=bar) # lookahead: 'foo' only if followed by 'bar'foo(?!bar) # negative lookahead: 'foo' NOT followed by 'bar'(?<=foo)bar # lookbehind: 'bar' only if preceded by 'foo'(?<!foo)bar # negative lookbehind: 'bar' NOT preceded by 'foo'# Common flags/pattern/i # case-insensitive/pattern/g # global (find all matches)/pattern/m # multiline (^ and $ match per line)/pattern/s # dotall (. matches newline too)
Backreferences & Conditional Patterns
Reuse previously captured text within the same pattern and branch on whether a group matched.
<(\w+)>.*?</\1> # match matching HTML/XML tag pairs via backreference(['"]).*?\1 # match a string quoted with either ' or ", using \1 for the closing quote(\w+)\s+\1 # find duplicated consecutive words ("the the")# Conditional pattern (PCRE/.NET): (?(1)yes|no) branches on whether group 1 matched(\()?\d+(?(1)\)|) # optional leading '(' requires a matching trailing ')'# Recursive pattern (PCRE) to match balanced parentheses\((?:[^()]|(?R))*\)
Atomic Groups & Possessive Quantifiers
Prevent catastrophic backtracking by disabling backtracking into a subgroup once it has matched.
# Vulnerable to ReDoS: nested quantifier + no anchor on failure path^(a+)+$ # "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!" hangs# Fixed with an atomic group (PCRE/.NET): once (a+) matches, no backtracking into it^(?>a+)+$# Fixed with a possessive quantifier (PCRE, Java): a++ never gives back characters^a++$# Same idea applied to a realistic pattern: greedy prefix before a suffix search^(?>[\w.%+-]+)@(?>[\w.-]+)\.[A-Za-z]{2,}$# Rule of thumb: any (X+)+ , (X*)* , or (X|XY)+ shape on attacker-controlled# input is a ReDoS candidate -- rewrite with atomic groups or possessive quantifiers
Unicode Property Escapes
Match by Unicode category/script instead of ASCII ranges for correctness with international text.
\p{L} # any kind of letter from any language\p{Lu} \p{Ll} # uppercase letter / lowercase letter\p{N} # any kind of numeric character\p{P} # punctuation\p{Emoji} # emoji (engine-dependent support)\p{Script=Han} # CJK ideographs\P{L} # negation: anything that is NOT a letter# JS requires the /u flag for property escapes/^\p{L}+$/u.test("café") // true/^[A-Za-z]+$/.test("café") // false -- ASCII-only class misses 'é'
Regex Engine Gotchas Across Languages
The same-looking pattern can behave differently depending on the engine/runtime.
- JS lookbehind- supported since ES2018 but requires fixed or bounded-length quantifiers in some engines; no recursion, no possessive quantifiers
- Python re vs regex module- stdlib `re` lacks variable-length lookbehind and atomic groups; the third-party `regex` module adds both plus possessive quantifiers
- POSIX ERE (grep -E, awk)- no backreferences, no lookaround, no non-greedy quantifiers -- leftmost-longest match semantics differ from PCRE's leftmost-first
- Java/`.NET`- support named groups, atomic groups, possessive quantifiers, and balancing groups (`.NET` only) for nested/recursive-like matching
- RE2 / Go regexp- deliberately excludes backreferences and lookaround to guarantee linear-time matching (no ReDoS risk), at the cost of expressive power
- Global flag statefulness (JS)- a `/g` regex object keeps `lastIndex` between calls to `.exec()`/`.test()`, which silently skips matches if you reuse the same object
- Multiline `$` before `\n`- in most engines `$` matches just before a trailing newline even without the multiline flag, tripping up strict end-of-string checks
Advanced Split & Replace
Capture groups inside split() keep the delimiters, and replace() callbacks enable programmatic substitution.
// Keep the delimiter by wrapping it in a capture group"a1b2c3".split(/(\d)/)// -> ['a', '1', 'b', '2', 'c', '3', '']// Replace using named groups"2026-07-21".replace( /(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/, "$<d>/$<m>/$<y>") // -> "21/07/2026"// Replace with a function for computed substitutions"price: 9, 12, 45".replace(/\d+/g, (match) => Number(match) * 2)// -> "price: 18, 24, 90"// matchAll for every match plus its capture groups (JS, requires /g)for (const m of "a=1, b=2".matchAll(/(\w)=(\d)/g)) { console.log(m[0], m[1], m[2], m.index)}
Avoid nested quantifiers like `(a+)+` on untrusted input — they can cause catastrophic backtracking (ReDoS); prefer possessive quantifiers, atomic groups, or a non-backtracking engine when the pattern must run on user-supplied data.