Regex Lookahead/Lookbehind Reference

Positive/negative lookahead and lookbehind syntax across PCRE, JS, Python, .NET.

Reference for zero-width regex lookaround assertions — positive and negative lookahead and lookbehind — with the exact syntax per flavor and notes on variable-length lookbehind support. It runs free in your browser on Gera Tools, with nothing uploaded.

Last updated Source: Gera Tools

What is a zero-width assertion?

A zero-width assertion matches a position rather than characters, so it consumes no input. Lookaround assertions test what comes before or after the current position and then the regex engine continues from where it was, never advancing past the asserted text.

Lookaround assertions

Lookahead and lookbehind are zero-width assertions: they check whether a pattern matches just after (lookahead) or just before (lookbehind) the current position without consuming any characters. They let you match a position defined by its context — for example, a digit that is followed by a currency symbol, or a word not preceded by a backslash. This reference gives the syntax for each of the four forms across PCRE, JavaScript, Python and .NET.

How it works

There are four assertions, built from two axes — direction and polarity:

(?=...)   positive lookahead   — next text DOES match
(?!...)   negative lookahead   — next text does NOT match
(?<=...)  positive lookbehind  — preceding text DOES match
(?<!...)  negative lookbehind  — preceding text does NOT match

Because they are zero-width, the engine evaluates the inner pattern, records success or failure, then resumes at the original position. A classic use is splitting on context without consuming a delimiter, e.g. inserting thousands separators with (?<=\d)(?=(\d{3})+$).

Syntax table by engine

AssertionPCREJavaScriptPython re.NET
Positive lookahead(?=...)(?=...)(?=...)(?=...)
Negative lookahead(?!...)(?!...)(?!...)(?!...)
Positive lookbehind(?<=...)(?<=...)(?<=...)(?<=...)
Negative lookbehind(?<!...)(?<!...)(?<!...)(?<!...)

The four tokens are identical in all four engines — the difference is in what widths of lookbehind each one accepts.

Variable-length lookbehind support

This is the most common portability problem. Some engines require the lookbehind pattern to have a fixed, knowable length.

EngineVariable-length lookbehind
JavaScript (ES2018+)Allowed — (?<=\d+) works
.NETAllowed
PCRE2Allowed (PCRE1 required fixed width)
Python reFixed-width only — (?<=\d) yes, (?<=\d+) no
Python regex moduleAllowed

If you need (?<=\w+) or similar and are targeting Python’s built-in re, either rewrite as a capture group or switch to the third-party regex module.

Worked examples

1. Match a price number without the currency symbol

Pattern: (?<=\$)\d+(\.\d{2})?

This matches the digits in $12.99 without including the $ in the match result. The lookbehind asserts the dollar sign is present; the match is the number only.

2. Skip a word that follows “not”

Pattern: (?<!not )\bfast\b

Matches fast in “run fast” but not in “not fast”. The negative lookbehind checks what comes before the current position.

3. Password must contain a digit and a lowercase letter

Pattern: ^(?=.*\d)(?=.*[a-z]).+$

Two positive lookaheads are chained at the start anchor. Each one tests independently from position zero, so both must succeed for the overall match to succeed. This is the standard multi-condition password-policy idiom.

Tips and notes

  • Lookbehind width is the main portability trap: keep it fixed-width unless you know the engine (.NET, JS, PCRE2, Python regex) allows variable width.
  • Lookarounds do not capture by default; add inner groups only if you need the inspected text.
  • Chain assertions at one position — (?=.*\d)(?=.*[a-z]) is the standard password-policy idiom.
  • A negative lookahead at the end, like foo(?!bar), is the readable way to say “foo not immediately followed by bar”.
  • Lookarounds add backtracking cost in long strings; if performance matters on large input, benchmark against a capturing approach.