Regular Expression Reference
Regex syntax in one page: characters, quantifiers, groups, lookaround and flags, plus the traps that make a pattern quietly wrong or catastrophically slow.
Regex syntax is small. Almost all of the difficulty is in a handful of behaviours that are easy to get subtly wrong — greediness, anchors, and what a character class actually contains. This covers the syntax and then those traps.
Character classes#
| Pattern | Matches |
|---|---|
. | Any character except a newline, unless the dotall flag is set |
\d / \D | A digit / anything that is not a digit |
\w / \W | A word character — [A-Za-z0-9_] — / anything else |
\s / \S | Whitespace, including tabs and newlines / anything else |
[abc] | Any one of a, b or c |
[^abc] | Any one character that is not a, b or c |
[a-z0-9] | A range, or several ranges combined |
Inside a class, most metacharacters lose their meaning. [.] is a literal dot. Only ^ (first), ], \ and - (unless first or last) need escaping there.
Quantifiers#
| Pattern | Repeats the preceding item |
|---|---|
* | Zero or more times |
+ | One or more times |
? | Zero or once |
{3} | Exactly three times |
{2,} | Two or more times |
{2,5} | Between two and five times |
*? +? ?? {2,5}? | The same, but lazy — match as few as possible |
Quantifiers are greedy by default: they take as much as they can and give characters back only if the rest of the pattern fails. This is the source of more wrong patterns than anything else on the page.
Subject: <a>first</a> and <a>second</a>
<a>.*</a> matches the whole string (greedy)
<a>.*?</a> matches only <a>first</a> (lazy)Anchors and boundaries#
| Pattern | Asserts |
|---|---|
^ | Start of the string, or of a line with the multiline flag |
$ | End of the string, or of a line with the multiline flag |
\b | A word boundary — between a \w and a non-\w |
\B | Not a word boundary |
\A / \z | Absolute start / end of the string, ignoring multiline |
Groups#
| Pattern | Meaning |
|---|---|
(abc) | Capturing group — available afterwards as $1, \1 |
(?:abc) | Non-capturing: groups for structure without capturing |
(?<name>abc) | Named capture, read back by name |
a|b | Alternation — a or b |
\1 | Backreference: matches what group 1 already matched |
Lookaround#
Lookaround asserts what is next to the match without consuming it, so it does not appear in the result.
| Pattern | Meaning |
|---|---|
(?=abc) | Lookahead — what follows is abc |
(?!abc) | Negative lookahead — what follows is not abc |
(?<=abc) | Lookbehind — what precedes is abc |
(?<!abc) | Negative lookbehind — what precedes is not abc |
\d+(?= dollars) matches 100 in "100 dollars", not in "100 euros"
(?<=\$)\d+ matches 100 in "$100", and the $ stays out of the matchFlags#
| Flag | Effect |
|---|---|
i | Case insensitive |
g | Global — find every match, not just the first |
m | Multiline — ^ and $ match at line breaks |
s | Dotall — . also matches a newline |
u | Unicode mode |
x | Extended — ignore whitespace and allow comments in the pattern |
Catastrophic backtracking#
Nested quantifiers over overlapping alternatives can make matching take exponential time. A pattern that runs instantly on ten characters can hang a server on thirty. This is a genuine denial-of-service class, usually written ReDoS.
(a+)+$ against "aaaaaaaaaaaaaaaaaaaaaaaaaaX"
— the engine tries every way to split the a's before failingAvoid it by not nesting quantifiers over the same characters, by making inner groups possessive or atomic where the engine supports it ((?>a+)), and by never building a pattern out of untrusted input.
The traps worth remembering#
\dis not "0-9" in Unicode mode — with theuflag in some engines it matches digits from other scripts too. Use[0-9]when you mean ASCII..stops at newlines unless you set dotall. Multi-line input is where patterns silently match nothing.$can match before a trailing newline. Use\zwhen you truly mean the end.- A dot in a class is literal —
[.]needs no escape, and[\.]is the same thing written less clearly. - Do not parse HTML with regex. Nesting is not a regular language. Use a parser.
- Do not validate email addresses with regex. The grammar is far larger than anyone expects. Check for an
@with something either side, then send a confirmation email — which is the only real proof anyway.