Regex Cheat Sheet: Syntax Tables and Ready-Made Patterns
Regex syntax in tables, plus patterns tested before publishing.
No signup, no trackingA regular expression is a small pattern language for describing text. The syntax is dense by design — every character earns its place — which makes it fast to write and genuinely hard to remember between uses.
This is the lookup version: seven tables covering the syntax you actually reach for, followed by a set of ready-made patterns. Every pattern on this page was executed against passing and failing inputs before publication, and the syntax descriptions follow MDN’s Regular expressions reference.
Metacharacters
| Token | Meaning |
|---|---|
| . | Any character except a line terminator |
| \ | Escapes the next character, making it literal |
| | | Alternation — matches the left side or the right side |
| [ ] | Character class — matches one character from the set |
| [^ ] | Negated class — matches one character not in the set |
| ( ) | Capturing group |
| { } | Explicit repetition count |
These are the characters that do not stand for themselves. To match one literally, put a backslash in front of it: \. matches an actual full stop, while an unescaped . matches any character. That is the single most common mistake in patterns written for domain names and filenames, where an unescaped dot quietly matches far more than intended.
Quantifiers
| Token | Meaning | Example |
|---|---|---|
| * | Zero or more | a* |
| + | One or more | a+ |
| ? | Zero or one (optional) | a? |
| {n} | Exactly n times | \d{4} |
| {n,} | n or more times | \d{2,} |
| {n,m} | Between n and m times | \d{2,4} |
| *? +? ?? {n,m}? | Lazy: match as few as possible | <.+?> |
Quantifiers are greedy by default: they take as much as possible and then give characters back until the rest of the pattern fits. Adding ? makes them lazy. Against the input <a><b>, the pattern <.+> matches the whole string while <.+?> matches only <a>. This single distinction is behind most "why did my regex eat the whole line" questions.
Character classes
| Token | Matches |
|---|---|
| \d | A digit, 0 to 9 |
| \D | Anything that is not a digit |
| \w | Word character: A-Z, a-z, 0-9 and underscore |
| \W | Anything that is not a word character |
| \s | Whitespace: space, tab, newline, carriage return |
| \S | Anything that is not whitespace |
| [a-z] | Range — any lowercase letter |
| [^0-9] | Any character except a digit |
| \p{L} | Any Unicode letter — requires the u flag |
| \P{L} | Any character that is not a Unicode letter |
In JavaScript \w and \d are ASCII-only, so ^\w+$ rejects any word containing an accent or a non-Latin letter. For text in languages other than English, use \p{L} with the u flag instead. Python behaves the opposite way: \w and \d already include Unicode in str patterns unless you pass re.ASCII.
Anchors and boundaries
| Token | Asserts |
|---|---|
| ^ | Start of the input, or start of a line with the m flag |
| $ | End of the input, or end of a line with the m flag |
| \b | Word boundary — the edge between \w and \W |
| \B | Not a word boundary |
Anchors match a position, not a character, so they consume nothing. \bcat\b finds the word cat in "cat concat" exactly once, because the cat inside concat has a word character to its left.
Groups, alternation and backreferences
| Token | Meaning |
|---|---|
| ( ... ) | Capturing group, referenced by number |
| (?: ... ) | Non-capturing group — groups without capturing |
| (?<name> ... ) | Named capturing group |
| \1, \2 | Backreference to capture group 1, 2 ... |
| \k<name> | Backreference to a named group |
/(?<year>\d{4})-(?<month>\d{2})/ named groups: .groups.year gives "2026"
/(?:ab)+(c)/ group 1 is "c" — (?: ...) does not capture
/\b(\w+)\s+\1\b/ finds repeated words: "the the cat"Use non-capturing groups whenever you only need the grouping. Capture numbering is positional, so inserting one ordinary group near the start of a pattern silently renumbers every group after it and breaks whatever code reads match[3]. Named groups avoid that problem entirely.
Lookaround
| Token | Meaning |
|---|---|
| (?= ... ) | Positive lookahead — followed by |
| (?! ... ) | Negative lookahead — not followed by |
| (?<= ... ) | Positive lookbehind — preceded by |
| (?<! ... ) | Negative lookbehind — not preceded by |
/\d+(?= USD)/ matches 45 in "45 USD" but not in "45 EUR"
/(?<=\$)\d+/ matches 99 in "$99", excluding the dollar sign
/^(?=.*\d)(?=.*[a-z]).{8,}$/ at least one digit and one letter, 8+ charactersFlags
| Flag | Effect |
|---|---|
| g | Global — find all matches, not just the first |
| i | Case-insensitive |
| m | Multiline — ^ and $ match line boundaries |
| s | Dot-all — . also matches newlines |
| u | Unicode mode — enables \p{...} and code-point handling |
| y | Sticky — match only from lastIndex |
The m flag is narrower than its name suggests: it changes only what ^ and $ mean, and has no effect on the dot. To make . cross line breaks you need s. The two are independent and frequently both required when matching across multi-line input.
Ready-made patterns
| Matches | Pattern |
|---|---|
| Email (pragmatic) | ^[^\s@]+@[^\s@]+\.[^\s@]{2,}$ |
| IPv4 address | ^((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$ |
| ISO 8601 date | ^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$ |
| ISO 8601 date and time | ^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})?$ |
| 24-hour time | ^([01]\d|2[0-3]):[0-5]\d(:[0-5]\d)?$ |
| HTTP or HTTPS URL | ^https?://[^\s/$.?#][^\s]*$ |
| Hex color | ^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$ |
| URL slug | ^[a-z0-9]+(?:-[a-z0-9]+)*$ |
| UUID, versions 1 to 5 | ^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ |
| Integer | ^-?\d+$ |
| Decimal number | ^-?\d+(\.\d+)?$ |
| Duplicated word | \b(\w+)\s+\1\b |
| HTML tag | </?[a-zA-Z][^>]*> |
| Trailing whitespace (with m) | [ \t]+$ |
A word about the email pattern: it is deliberately loose, and that is the honest choice. RFC 5322 permits quoted local parts, comments and nested constructs that no readable regex captures, while the addresses people actually mistype are rejected by no regex at all. The WHATWG HTML standard sidesteps this by defining its own intentionally narrower pattern for input type="email" — which, notably, accepts a@b with no dot in the domain. Use a regex to catch typos, then confirm the address by sending mail to it. That is the only real validation.
Test these patterns
A table cannot tell you why a pattern fails on your particular input. The Regex Tester on this site highlights matches live as you type, lists each capture group with its contents, and lets you toggle flags to see the effect immediately — which is the fastest way to understand greedy versus lazy matching. It runs entirely in your browser, so patterns and test text never leave your device.