C.W.K.
Stream
Lesson 01 of 10 · published

What Are Zero-Width Assertions?

~10 min · zero-width, lookaround, fundamentals

Level 0Pattern-Curious
0 XP0/90 lessons0/15 achievements
0/100 XP to next level100 XP to go0% complete

An assertion that doesn't consume characters

You've already met one zero-width assertion: \b, the word boundary. It checks "am I at the boundary between word and non-word?" without consuming any character. That's what "zero-width" means — the assertion checks a property of a POSITION, not a CHARACTER.

Lookaround is the general form of this idea. (?=...) means "at this position, the following text matches ... — but don't move the cursor." The engine peeks ahead, confirms a match, then continues from where it was BEFORE the peek.

Why this matters

Without lookaround, you'd have to capture and consume context that you don't actually want in your match. Lookaround lets you say "this character matters, but only because of what surrounds it."

Concrete: "find numbers followed by 'kg' but only return the number." Without lookaround, you match the 'kg' and have to strip it. With lookahead: \d+(?=kg) matches the number AND asserts 'kg' follows, but the match is just the number.

The four flavors

Lookaround has four variants — two directions × two polarities:

  • (?=...)positive lookahead: "... follows here"
  • (?!...)negative lookahead: "... does NOT follow here"
  • (?<=...)positive lookbehind: "... precedes here"
  • (?<!...)negative lookbehind: "... does NOT precede here"

Each is covered in its own lesson next.

Code

Zero-width vs consuming·python
import re

# Without lookahead — match includes the 'kg'
re.findall(r'\d+kg', '5kg of flour, 2kg of sugar')
# ['5kg', '2kg']  — have to strip the 'kg' yourself

# With lookahead — match is just the number
re.findall(r'\d+(?=kg)', '5kg of flour, 2kg of sugar')
# ['5', '2']

# Word boundary — the original zero-width assertion
re.findall(r'\bcat\b', 'cat scatter cats')
# ['cat']  — the \b's match positions, not characters

External links

Exercise

Take any pattern in your code where you match more than you actually want and then trim with strip() or slicing. Rewrite it with lookahead/lookbehind so the match itself is exactly what you want.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.