breakitdown

Greedy vs lazy quantifiers: .* vs .*?

Answer

.* is greedy: it takes as much as it can, then backs off. .*? is lazy: it takes as little as it can, then extends. On say "hi" and "bye", "(.*)" captures hi" and "bye, while "(.*?)" captures hi and then bye.

flags: g all matches · i ignore case · m ^ $ per line · s . matches newlines · u Unicode

JavaScript ✓PCRE ✓

No catastrophic-backtracking patterns found. (A heuristic check: nested and adjacent repeats, overlapping alternatives.)

  1. \"The character '"' literallyliteral
  2. (…)Capturing group #1: remembers what it matchedgroup
  3. .Any character except a line breakcharacter type
  4. *?Repeat it zero or more times, as few as possible (lazy)quantifier
  5. \"The character '"' literallyliteral

1 capturing group.

Examples

Checked at build time in JavaScript and in Python's re.

InputMatches?
say "hi" and "bye"✓ match
""✓ match
no quotes here✗ no match
"unterminated✗ no match
he said "line one line two"✗ no match
'single quotes'✗ no match

Try your own text

One string per line: each line is tested on its own (the m flag is added here).

How it works

Both versions find the same starting quote. The difference is where they stop. Greedy .* first runs to the end of the line, then backtracks one character at a time until it finds a ". That is the last quote on the line. Lazy .*? tries zero characters first, then one more each time the next " fails to match, so it stops at the first closing quote.

A negated class is usually the better tool. "([^"]*)" cannot run past a quote at all, so there is nothing to backtrack, and it states the intent directly: the content is everything that is not a quote. Lazy quantifiers are the right choice when the end marker is longer than one character, such as <!--[\s\S]*?-->.

Variants

Gotchas

Related explainers

← Explain any regular expression