Regex · <code>(?=...)</code>
Lookahead and lookbehind
Lookaround asserts what comes before or after a position without consuming characters.
Key facts
| Positive lookahead | <code>(?=...)</code> |
|---|---|
| Negative lookahead | <code>(?!...)</code> |
| Positive lookbehind | <code>(?<=...)</code> |
| Negative lookbehind | <code>(?<!...)</code> |
| Consumption | None — zero width |
| JavaScript | Lookbehind since ES2018, variable length allowed |
| Python re | Lookbehind must be fixed width |
| PCRE | Lookbehind length rules vary by version |
Assertions that do not consume
\d+(?=px) matches the number in “12px” but not the “px”. Because the assertion is zero-width, a following pattern can still match the same characters. The negative form is the usual way to say “not followed by”.
Lookbehind is the mirror image: (?<=\$)\d+ matches digits preceded by a dollar sign. A common mistake is expecting lookaround to remove text from the match; it only tests a position.
Engine limits
Lookbehind is not equally capable everywhere. Python's re requires a fixed-width pattern inside lookbehind, so (?<=\w+) is rejected. JavaScript allows variable-length lookbehind, and recent PCRE versions have relaxed the old restriction, but older runtimes in production may not.
If a pattern must be portable, replace lookbehind with a capturing group and ignore the capture, or match the prefix and slice it in code. That is boring and it works everywhere.
Frequently asked questions
What is the difference between lookahead and lookbehind?
Lookahead tests the text after the current position; lookbehind tests the text before it. Neither consumes characters.
Why does Python reject my lookbehind?
The re module requires a fixed-width lookbehind. Use a capturing group or a different engine.
Does lookaround change the reported match?
No. Only the consumed part is reported; assertions affect whether the match succeeds, not its boundaries.