Lookahead and Lookbehind in Regex
Lookarounds let you assert that something does — or does not — appear before or after your match, without including it in the result. Here is each of the four forms with an example you can test.
A lookaround is a zero-width assertion: it checks a condition but consumes no characters, so the matched text does not include it.
The four forms
| Syntax | Name | Asserts |
|---|---|---|
(?=X) | Positive lookahead | What follows is X |
(?!X) | Negative lookahead | What follows is not X |
(?<=X) | Positive lookbehind | What precedes is X |
(?<!X) | Negative lookbehind | What precedes is not X |
Positive lookahead — price without the currency symbol
\d+(?=\s*USD)
Matches 42 in 42 USD but not the USD. The number is captured; the currency is only asserted.
Negative lookahead — a word not followed by another
foo(?!bar)
Matches foo in foobaz but not in foobar.
Positive lookbehind — amount after a symbol
(?<=\$)\d+(\.\d{2})?
Matches 19.99 in $19.99 without including the dollar sign.
Negative lookbehind — a number not preceded by a minus
(?<!-)\b\d+\b
Matches positive numbers but skips -5.
A classic combo: password rules
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$
Stacked lookaheads each check for one requirement (a lowercase letter, an uppercase letter, a digit) before .{8,} checks the length. This is how most "strong password" patterns work.
Engine support
Lookahead works everywhere. Lookbehind is supported in modern JavaScript (V8/Node, all current browsers), PCRE, Python, .NET, Java and Go's regexp2, but older or POSIX engines may reject it. Variable-length lookbehind is allowed in .NET and modern JS but not in classic PCRE.
FAQ
Does a lookahead consume characters?
No. It is zero-width — after a lookahead succeeds, the engine's position does not move, so the asserted text is not part of the match and can be matched again by later tokens.
Can I use variable-length lookbehind?
It depends on the engine. .NET and modern JavaScript allow it. Classic PCRE (and therefore many PHP setups) require fixed-length lookbehind, so (?<=abc) is fine but (?<=a+) is not.
Why use a lookahead instead of a capturing group?
When you want to require context but exclude it from the match — for example to grab a number that is followed by a unit without capturing the unit itself.