Regex › Regex Fundamentals

Quantifiers: greedy, lazy, and dangerous

5 min read Intermediate 6 sections

Quantifiers control how many times something matches. You’ve met the basics; now we look at how they behave — greedy by default, optionally lazy — because getting this wrong produces both incorrect matches and, in the worst case, a denial-of-service vulnerability called ReDoS. This is where regex stops being just pattern-matching and starts having security consequences.

You'll learn to

  • Control match counts with the quantifiers
  • Tell greedy from lazy matching
  • Recognise the patterns that cause catastrophic backtracking

The quantifiers

*        zero or more
+        one or more
?        zero or one (optional)
{n}      exactly n
{n,}     n or more
{n,m}    between n and m

These attach to whatever comes before them. So a+ is one or more a, [0-9]{3} is exactly three digits, colou?r matches both “color” and “colour” (the u is optional).

Greedy by default

By default, quantifiers are greedy — they match as much as possible. This causes a classic surprise:

Pattern:  <.+>
Input:    <b>bold</b>
Greedy match: <b>bold</b>   ← the WHOLE thing, not just <b>

You wanted <b>, but .+ greedily grabbed everything up to the last >. The greedy quantifier matches as much as it can while still allowing the overall pattern to succeed.

Lazy matching fixes it

Add a ? after a quantifier to make it lazy — match as little as possible:

Pattern:  <.+?>
Input:    <b>bold</b>
Lazy match: <b>   ← just the first tag, as intended

The +? matches the minimum needed. Greedy versus lazy is one of the most practical distinctions in regex — when a pattern grabs too much, making the quantifier lazy is usually the fix.

The dangerous side: catastrophic backtracking

Here’s where quantifiers become a security issue. Certain patterns make the regex engine try an exponential number of combinations on certain inputs — freezing for seconds, minutes, or effectively forever.

Dangerous:  (a+)+$
Input:      aaaaaaaaaaaaaaaaaaaaaaaa!   (many a's, then a non-match)

The nested quantifier — a repeat of a repeat — means the engine can split those as in a huge number of ways, and on input that almost matches, it tries them all before giving up. This is catastrophic backtracking, and it’s the basis of ReDoS (Regular expression Denial of Service).

Checkpoint

A greedy pattern that matches an angle bracket, then any characters, then a closing angle bracket grabs too much when run against a string with two HTML tags — it spans to the last closing bracket. Why, and how do you fix it to match just the first tag?

Try it yourself

Mentally run the pattern with a greedy quote-matcher against the text: she said “hello” and “bye”. A greedy pattern between quotes captures from the first quote to the last, grabbing everything in between. Then make it lazy and see how it captures each quoted phrase separately. This is the single most common greedy/lazy lesson in practice.

Summary

Quantifiers (*, +, ?, {n}, {n,m}) control how many times something matches, and are greedy by default — matching as much as possible, which often grabs too much between delimiters. Adding ? makes them lazy (match as little as possible), usually the fix. Nested quantifiers create catastrophic backtracking, the basis of ReDoS — a real denial-of-service vulnerability when an app runs such a regex on attacker-controlled input. Watch for these shapes both when writing and auditing patterns.

Key takeaways

  • Quantifiers are greedy by default; add ? to make them lazy.
  • Use lazy quantifiers to match content between delimiters correctly.
  • Nested quantifiers like a quantified-and-repeated group cause catastrophic backtracking.
  • ReDoS is a real, exploitable DoS — a high-impact finding in validation code.

Quick quiz

Next, groups and capturing — how you extract specific pieces of a match, which is the heart of pulling data out of text.

Was this lesson helpful?