Home/Blog/What is the difference between greedy and lazy quantifiers?
Developer Tools

What is the difference between greedy and lazy quantifiers?

Understand greedy vs lazy quantifiers in regex, how they affect matching behavior, and when to use each approach.

By Inventive HQ Team
What is the difference between greedy and lazy quantifiers?

Greedy vs Lazy Quantifiers in Regex

One of the most fundamental but frequently misunderstood concepts in regular expressions is the distinction between greedy and lazy quantifiers. This concept profoundly affects how patterns match text, often producing unexpected results until you understand the underlying logic. The difference between matching "as much as possible" versus "as little as possible" determines whether your regex captures an entire document or precisely the text you intended.

By default, regex quantifiers are greedy—they match as much text as possible while still allowing the overall pattern to match. This behavior is often correct, but sometimes you need the opposite: to match as little text as possible. Understanding when to use each approach and how to switch between them is essential for writing correct regex patterns.

What Makes a Quantifier "Greedy"?

Greedy quantifiers match the maximum possible amount of text:

Greedy Quantifiers:

*   - Zero or more (greedy)
+   - One or more (greedy)
?   - Zero or one (greedy)
{n,m} - Between n and m (greedy)

When a regex engine encounters a greedy quantifier, it first tries to match as much as possible. Only if the rest of the pattern fails to match does it backtrack (reduce the match) and try again.

Example: The Problem with Greedy

Text: <div>Hello</div><span>World</span>
Pattern: <.*>

You might expect this to match <div> or <span>, but instead:

Match Result: <div>Hello</div><span>World</span>
Why: .* greedily matches EVERYTHING between < and >
     It matches from the first < through the last >

The .* matches div>Hello</div><span>World</span> leaving only the final > for the pattern to complete.

What Makes a Quantifier "Lazy"?

Lazy (non-greedy) quantifiers match the minimum possible amount of text. Make any quantifier lazy by adding ? after it:

Lazy Quantifiers:

*?  - Zero or more (lazy)
+?  - One or more (lazy)
??  - Zero or one (lazy)
{n,m}? - Between n and m (lazy)

When a regex engine encounters a lazy quantifier, it first tries to match as little as possible. Only if the overall pattern fails does it expand and try again.

Example: The Solution with Lazy

Text: <div>Hello</div><span>World</span>
Pattern: <.*?>

Now the pattern matches:

Match 1: <div>
Match 2: </div>
Match 3: <span>
Match 4: </span>
Why: .*? matches minimally, stopping at the first > after <

This is typically what you want when matching HTML/XML tags.

Detailed Comparison

How Greedy Matching Works

When a regex engine encounters a greedy quantifier:

  1. Expand: Match as much as possible
  2. Check: Does the rest of the pattern match?
  3. Result: If yes, done! If no, backtrack and try again
  4. Backtrack: Reduce match by one character
  5. Repeat: Go back to step 2

Example with Trace:

Text: "aaab"
Pattern: a+b

Step 1: a+ matches "aaa" (greedy, maximum)
Step 2: Try to match "b" at position after "aaa"
Step 3: "b" matches!
Result: "aaab" ✓

How Lazy Matching Works

When a regex engine encounters a lazy quantifier:

  1. Minimize: Match as little as possible
  2. Check: Does the rest of the pattern match?
  3. Result: If yes, done! If no, expand and try again
  4. Expand: Match one more character
  5. Repeat: Go back to step 2

Example with Trace:

Text: "aaab"
Pattern: a+?b

Step 1: a+? matches "a" (lazy, minimum)
Step 2: Try to match "b" at next position
Step 3: "a" doesn't match "b", expand
Step 4: a+? matches "aa"
Step 5: Try to match "b" at next position
Step 6: "a" doesn't match "b", expand
Step 7: a+? matches "aaa"
Step 8: Try to match "b" at next position
Step 9: "b" matches!
Result: "aaab" ✓ (same as greedy here)

When Greedy and Lazy Produce Different Results

Extracting Content from Tags

Text: <div>Hello</div><span>World</span>
Pattern (Greedy): <div>(.*)</div>
Pattern (Lazy): <div>(.*?)</div>

Greedy Match:

Captured: Hello</div><span>World
Result: Everything between <div> and the LAST </div>

Lazy Match:

Captured: Hello
Result: Everything between <div> and the NEXT </div>

The lazy version correctly captures just the content.

Extracting Quoted Strings

Text: "hello" and "world"
Pattern (Greedy): "(.*)_"
Pattern (Lazy): "(.*?)"

Greedy Match:

Captured: hello" and "world
Result: Content between first " and last "

Lazy Match:

Match 1: "hello"
Match 2: "world"
Result: Each quoted string separately

Multiple Alternatives

Text: aaaaab
Pattern (Greedy): a*?ab
Pattern (Lazy): a*ab

Both match "aaaaab", but for different reasons—the greedy version backtracks to find the match.

Practical Examples

Example 1: HTML Tag Extraction

// Greedy - WRONG
let html = "<h1>Title</h1><p>Content</p>";
let match = html.match(/<.*>/);
// Result: "<h1>Title</h1><p>Content</p>" (entire string!)

// Lazy - CORRECT
let match = html.match(/<.*?>/);
// Result: "<h1>" (just first tag)

// Get all tags with lazy quantifier
let tags = html.match(/<.*?>/g);
// Result: ["<h1>", "</h1>", "<p>", "</p>"]

Example 2: Extract Quoted Strings

import re

text = 'She said "hello" and "goodbye"'

# Greedy - WRONG
matches = re.findall(r'"(.*)"', text)
# Result: ['hello" and "goodbye'] (too much)

# Lazy - CORRECT
matches = re.findall(r'"(.*?)"', text)
# Result: ['hello', 'goodbye'] (correct)

Example 3: CSV-like Data

Text: name,age,"Smith, John",30
Pattern (Greedy): "(.*)_"
Pattern (Lazy): "(.*?)"

Greedy would match too much (from first quote to last quote) Lazy correctly matches quoted fields individually

Example 4: URL Parameter Extraction

// Greedy - WRONG
let url = "?param=value1&other=value2";
let params = url.match(/=.*/);
// Result: "=value1&other=value2" (too much)

// Lazy - CORRECT
let param = url.match(/=.*?(&|$)/);
// Result: "=value1&" (correct, uses lookahead)

Performance Implications

Greedy Matching:

  • First tries maximum match
  • Backtracks if needed
  • Generally faster when match succeeds quickly

Lazy Matching:

  • First tries minimum match
  • Expands if needed
  • Generally faster when pattern is short

Catastrophic Backtracking Risk:

  • Greedy quantifiers with complex patterns can cause exponential backtracking
  • Lazy quantifiers reduce this risk somewhat
  • Better solution: Be specific with patterns, avoid nested quantifiers

Example of Problematic Greedy Pattern

(a+)+b   // Dangerous! Can cause catastrophic backtracking

If the text is "aaaaaaaaaaaa" (no 'b'), the engine tries exponential combinations.

Better:

(a)+b    // Better
a+b      // Best

Choosing Greedy vs Lazy

Use Greedy When:

  • You want to match as much as possible
  • You're looking for the end of text: .*$
  • You want the entire content: .* for whole-line matching
  • Performance is critical and pattern is simple

Use Lazy When:

  • You need to stop at a specific point
  • Extracting content between delimiters: <.*?>
  • Matching quoted strings: ".*?"
  • Matching multiple separate items: .*? with global flag

Decision Questions:

  1. "Do I want to match UP TO the next occurrence of X?" → Use lazy
  2. "Do I want to match EVERYTHING until the pattern ends?" → Use greedy
  3. "Am I unsure?" → Try lazy first (more intuitive for most people)

Converting Between Greedy and Lazy

From Greedy to Lazy: Add ? after the quantifier

.* → .*?
.+ → .+?
.? → .??
.{3,5} → .{3,5}?

Testing the Difference:

  1. Try your pattern with greedy quantifier
  2. If it matches too much, change to lazy
  3. If it matches too little, change to greedy

Common Mistakes

Forgetting Lazy is an Option

WRONG: <.*> trying to extract tags (matches everything)
RIGHT: <.*?> (matches single tags)

Using Lazy Unnecessarily

Inefficient: a+?b (lazy quantifier adds complexity)
Better: ab (if you want single 'a')

Combining Greedy with Greedy

WRONG: .*.*  (two greedy quantifiers, wasteful)
RIGHT: .* (single greedy quantifier)

Summary Table

TaskPatternTypeWhy
Match everything.*GreedyWant maximum match
Extract tag content<(.*?)>LazyStop at closing tag
Match quoted string"(.*?)"LazyStop at closing quote
Match to end of line.*$GreedyWant everything until end
Match pairsa(.*?)bLazyStop at first b
Match all words\w+GreedyMatch complete words

Conclusion

Greedy and lazy quantifiers are complementary tools in your regex toolkit. By default, quantifiers are greedy—they match as much as possible. When you need to match minimally (stopping at the first opportunity), add ? to make the quantifier lazy. Most text extraction and delimiter-based matching tasks benefit from lazy quantifiers. When in doubt, start with lazy quantifiers for pattern matching between delimiters, and use greedy quantifiers for matching to the end of text or when you specifically want maximum matching. Testing patterns with sample data reveals which approach works correctly for your specific case.

Need Expert IT & Security Guidance?

Our team is ready to help protect and optimize your business technology infrastructure.