Download Cheat sheet PDF 12 pages · syntax, editors, patterns, Unicode, performance, debugging
Concepts May 11, 2026

Greedy vs lazy quantifiers in regex

.* and .*? look almost identical, but they match very different things.

The short answer

Greedy (the default): match as much as possible, then back off if needed.
Lazy (add ? after the quantifier): match as little as possible, then extend if needed.

Symbols:

  • *, +, ?, {n,m} — greedy
  • *?, +?, ??, {n,m}? — lazy

The classic example

Input:   "foo" and "bar"
Greedy:  ".*"     →  matches `"foo" and "bar"` (THE WHOLE THING)
Lazy:    ".*?"    →  matches `"foo"` (stops at first ")

The greedy version starts at the first quote and tries to grab everything. The regex engine then notices there's no closing quote at the end, so it backs off one character at a time until it finds one — which happens at the last ". So it matches the whole sandwich.

The lazy version starts with zero characters and grows by one until the closing quote is found — which happens at the first closing quote. So it matches just "foo".

When to use each

Greedy: when you want a complete match

Greedy is the default for a reason. When matching identifiers, numbers, or anything contiguous, greedy is what you want:

\d+      grabs all digits in a row
\w+      grabs the entire word
[a-z]+   grabs the run of lowercase letters

Lazy: when you want to stop at the first delimiter

Use lazy when there's a closing marker and you want the smallest content between them:

<.*?>          each HTML tag, one by one
".*?"          each quoted string, one by one
\(.*?\)        each parenthesized phrase

The better alternative: negated character class

Often, what people reach for "lazy" to fix is better done with a negated class. Compare:

Lazy:           ".*?"
Negated class:  "[^"]*"

Both match a string between quotes. The negated class is usually preferable because:

  • Clearer intent — "match anything except a quote" reads more naturally than "match anything but stop early."
  • No backtracking — the negated class consumes characters that definitely belong, never has to back off.
  • Slightly faster — though for most patterns this won't matter.

When lazy isn't enough

Lazy quantifiers handle most cases but can still backtrack. For truly catastrophic backtracking (where the engine explores exponentially many paths on input that almost matches), you need stronger tools:

  • Possessive quantifiers (*+, ++, ?+) — never give back what they matched, eliminate backtracking entirely. PCRE, Java, Python 3.11+.
  • Atomic groups ((?>...)) — same idea applied to a group. PCRE, Java.

The takeaway

Greedy is the default and usually right. Reach for lazy when you have a closing delimiter and want the smallest match — but consider a negated character class first; it's often clearer and faster.

If you find yourself in performance trouble or seeing ReDoS warnings, look at possessive quantifiers or atomic groups (if your flavor supports them).


Related reading


Try this pattern in the explainer

Paste any regex into the live explainer and see what each token means, with example matches in real time.

Open the regex explainer →