regex lookahead and lookbehind — (?=...) (?<=...) (?!...) (?<!...)
Quick Answer
# Positive lookahead — match "foo" only when followed by "bar"
/foo(?=bar)/ matches "foobar" → "foo"
# Negative lookahead — match "foo" NOT followed by "bar"
/foo(?!bar)/ matches "fooX" → "foo"
# Positive lookbehind — match "bar" only when preceded by "foo"
/(?<=foo)bar/ matches "foobar" → "bar"
# Negative lookbehind — match "bar" NOT preceded by "foo"
/(?<!foo)bar/ matches "Xbar" → "bar"
Usage
You need to assert context around a match without including that context in the captured text.
Other causes & fixes
JavaScript examples
// Extract price number (not the $ sign)
const prices = '$10 $20 $30';
const matches = prices.match(/(?<=\$)\d+/g);
// ['10', '20', '30']
// Match word NOT followed by "px"
'10px 20em 30px'.match(/\d+(?!px)/g)
// ['20'] — only the em value
Python examples
import re
# Positive lookahead — match digits before 'px'
re.findall(r'\d+(?=px)', '10px 20em 30px')
# ['10', '30']
# Lookbehind — match value after 'color: '
re.search(r'(?<=color: )\w+', 'color: red').group()
# 'red'
Variable-length lookbehind (Python 3.9+, PCRE)
# Python <3.9 requires fixed-length lookbehind
# (?<=ab|abc) → error in older Python
# (?<=(?:a|ab)) → also not allowed
# Workaround with a group
re.sub(r'(prefix_?)(\w+)', r'\g<2>', 'prefix_value')
Related