C.W.K.
Stream
Lesson 07 of 12 · published

Lazy (Non-Greedy) Matching — *? +? ??

~10 min · lazy, non-greedy, engine

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

Add ? to make a quantifier minimal-match

To flip greedy → lazy, append ? to the quantifier:

  • *? — 0 or more, but as few as possible
  • +? — 1 or more, but as few as possible
  • ?? — 0 or 1, prefer 0
  • {n,m}? — between n and m, prefer n

Lazy quantifiers consume the MINIMUM and only extend if the rest of the pattern fails. Backwards from greedy, but same backtracking machinery.

The HTML disaster, fixed

Greedy .* ate both <p> blocks as one match. Lazy .*? matches each one separately — it stops at the first </p> instead of the last.

Performance note

Lazy is not free. The engine still backtracks; it just backtracks in the opposite direction (extending instead of shrinking). For some patterns lazy is faster, for others greedy is. The bigger win is correctness — lazy matches what you usually want.

The negated-class alternative

Often the cleanest fix is neither lazy nor greedy: it's a negated class. <p>([^<]*)</p> says "capture everything that's NOT a <" — no backtracking, no surprise. Track 8 will show this is the secret to ReDoS-safe patterns.

Code

Lazy fixes the greedy disaster·python
import re

html = '<p>first</p> <p>second</p>'

# Greedy — wrong
re.findall(r'<p>.*</p>', html)
# ['<p>first</p> <p>second</p>']

# Lazy — right
re.findall(r'<p>.*?</p>', html)
# ['<p>first</p>', '<p>second</p>']

# Better still — negated class (no backtracking)
re.findall(r'<p>[^<]*</p>', html)
# ['<p>first</p>', '<p>second</p>']

# Quoted strings — same lesson
re.findall(r'"[^"]*"', '"a" "b" "c"')
# ['"a"', '"b"', '"c"']  — clean and ReDoS-safe

External links

Exercise

Take the same input <p>first</p> <p>second</p> and write THREE patterns that extract each <p>...</p> separately: one with lazy, one with negated class, and one with the lookahead <p>(.*?)(?=</p>). Time them on a 100KB HTML file.

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.