Back to Blog
Reference 2026-04-25

Regex Cheatsheet for 2026

Quick reference for regex patterns, flags, and tested examples.

Regex syntax is dense enough that even people who use it weekly forget the details. This cheatsheet covers the constructs you reach for daily, plus the 2026-era additions — Unicode property escapes and the v flag — and the performance trap that takes production services down.

Character Classes

  • \d / \D — digit / non-digit
  • \w / \W — word character (letters, digits, underscore) / everything else
  • \s / \S — whitespace / non-whitespace
  • . — any character except newline (add the s flag to include newlines)
  • [abc], [a-z], [^abc] — set, range, negated set
  • \p{L}, \p{Script=Hangul} — Unicode property escapes (require the u or v flag); \w is ASCII-only, so use \p{L} when matching international text

Quantifiers

  • ? — zero or one
  • + — one or more
  • {n}, {n,}, {n,m} — exact, at-least, bounded counts
  • a* — zero or more of a
  • Greedy by default: quantifiers take the longest match that still allows the rest of the pattern to succeed
  • Append ? for the lazy version — +? takes the shortest match; the classic use is <.+?> to match one tag instead of an entire line of tags

Anchors and Boundaries

  • ^ and $ — start and end of string; with the m flag, of each line
  • \b / \B — word boundary / non-boundary; \bcat\b matches "cat" but not "category"
  • Boundary definitions follow \w, so they are also ASCII-only by default

Groups and Backreferences

  • (abc) — capturing group, numbered left to right
  • (?:abc) — non-capturing; use for pure grouping to keep numbering stable and save allocations
  • (?<name>abc) — named capture, read via match.groups.name
  • \1 and \k<name> — backreferences; (["'])(.?)\1 matches a string quoted by either quote type

Lookarounds

  • (?=x) / (?!x) — positive / negative lookahead
  • (?<=x) / (?<!x) — positive / negative lookbehind (JavaScript since ES2018)
  • Lookarounds consume no characters, which makes them ideal for password rules and insert-position matching, e.g. thousands separators: /\B(?=(\d{3})+(?!\d))/g

Flags

  • g — all matches, not just the first; stateful with exec, a classic source of alternating-match bugs
  • i — case-insensitive
  • m^ and $ match at line breaks
  • s — dot matches newline
  • u — proper Unicode: astral characters are one unit, property escapes enabled
  • v (ES2024) — everything u does, plus set operations like [\p{L}--[aeiou]] (letters except vowels) and string properties like \p{RGI_Emoji}
  • y — sticky, match exactly at lastIndex; the building block for hand-written lexers

Battle-Tested Patterns

Email (pragmatic):  ^[\w.+-]+@[\w-]+\.[\w.-]+$

IPv4 octet-exact: ^(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|1?\d?\d)$

ISO 8601 date: ^\d{4}-\d{2}-\d{2}$

UUID v4: ^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$

URL slug: ^[a-z0-9]+(?:-[a-z0-9]+)*$

Hex color: ^#(?:[0-9a-fA-F]{3}){1,2}$

The email pattern is deliberately loose. A fully RFC 5321-correct pattern is enormous and still cannot tell you whether the mailbox exists — validate format loosely, then confirm by sending mail.

Catastrophic Backtracking

The one regex behavior that causes outages. Nested quantifiers over overlapping alternatives — shapes like (a+)+b or (\w|\d)+$ — force the backtracking engine to try exponentially many partitions when a long input almost matches but fails at the end. A 40-character malicious string can pin a CPU core for minutes; this is ReDoS.

Defenses:

  • Make alternatives non-overlapping and avoid quantifying an already-quantified group
  • Prefer explicit character classes over .
  • Linear-time engines (RE2, Rust's regex crate) are immune by construction — the trade-off is no backreferences or lookarounds
  • Never run user-supplied patterns against user-supplied input on a request thread; if you must, apply a timeout in a worker

Habits That Pay Off

  • Compile once, reuse: a regex literal inside a hot loop re-creates state in some engines
  • Anchor patterns whenever position is known — ^ fails non-matches immediately instead of scanning
  • Comment complex patterns by composing them from named source strings
  • Test with adversarial inputs: empty string, very long near-matches, Unicode beyond ASCII

Engines Differ More Than Syntax

The same pattern can behave differently across engines, and knowing which family you are on prevents subtle porting bugs:

  • Backtracking engines — JavaScript (V8's Irregexp), PCRE2, Python's re, Java's java.util.regex. Full feature set, exponential worst case.
  • Linear-time engines — RE2 (used by Go's regexp), Rust's regex crate. Guaranteed O(n) matching via automata; no backreferences, no lookaround.
  • Hybrids — .NET offers a NonBacktracking mode; PCRE2 ships a DFA matching function.

Feature gaps to check before copying a pattern from Stack Overflow: lookbehind (unsupported in Safari before 16.4, absent in Go/RE2), possessive quantifiers and atomic groups (PCRE and Java only — JavaScript emulates them with lookahead-plus-backreference tricks), and \p property escape coverage. In JavaScript specifically, prefer matchAll for iterating global matches — it sidesteps the shared lastIndex state that makes exec loops fragile — and remember that String.replace with a plain string replaces only the first occurrence; you need a regex with the g flag.

Iterate on patterns live with match highlighting in the Regex Tester at sdk.is/regex-tester.