Skip to content

Chapter 2 of 8

Quantifiers: Greedy, Lazy, Possessive, and Exact

Quantifiers tell the engine how many times to repeat the preceding token. The three fundamental quantifiers are * for zero or more, + for one or more, and ? for zero or one. The difference between * and + matters whenever the pattern might match an empty string: colou?r accepts both color and colour because the ? makes the u optional, while a quantifier like \d+ requires at least one digit and would reject an empty input. When you need a specific count, the brace syntax applies: {3} matches exactly three, {3,} matches three or more, and {3,5} matches between three and five inclusive. + is precisely {1,}; they are interchangeable and you can choose whichever reads better in context.

By default, quantifiers are greedy, meaning they consume as many characters as possible while still leaving room for the remainder of the pattern to succeed. If that remainder fails, the engine backtracks one character at a time until either the overall pattern matches or all possibilities are exhausted. This is essential for correctness, but it has a hidden cost: on adversarial inputs, backtracking can become exponential. To force a quantifier to be lazy, append ? as in .*?, which matches as few characters as possible and expands only as the surrounding pattern demands. The lazy form is the right choice when you want the shortest match, such as extracting a single HTML tag with <.*?>.

Greedy and lazy quantifiers both backtrack; possessive quantifiers do not. A possessive quantifier, written with a trailing + as in .*+, refuses to give characters back once consumed, which makes matching faster and can prevent catastrophic backtracking on patterns like (a+)+$. Atomic groups, written (?>...), achieve the same effect for a whole subexpression: once the group has matched, the engine treats its match as indivisible and never releases characters, even when the surrounding pattern would otherwise force backtracking. Atomic groups are supported in PCRE, Java, and Python 3.11+, and they are an essential tool when you need to combine expressiveness with predictable performance.

All chapters
  1. 1Character Matching: Metacharacters, Shorthands, and Classes
  2. 2Quantifiers: Greedy, Lazy, Possessive, and Exact
  3. 3Anchors and Boundaries
  4. 4Groups, Captures, and Backreferences
  5. 5Lookaround Assertions
  6. 6Flags, Modes, and Engine Features
  7. 7Practical Recipes for Validation and Extraction
  8. 8Performance and Engine Internals

Drill it

Reading is not remembering. These come from the Regex Patterns Every Developer Should Know deck:

Q

What does the regex metacharacter <code>.</code> match by default?

A single character of any kind except the newline character (\n). To make it also match newlines, use the s (dotall / single-line) flag, e.g. /foo.bar/s in Java...

Q

Which character class matches any single digit in most regex flavors?

[0-9] or the shorthand \d. \d is equivalent to [0-9] in ASCII; with Unicode (u flag in JS, default in Python 3 str) it matches any Unicode decimal digit, e.g. A...

Q

What does the shorthand <code>\w</code> match?

Any word character. In ASCII mode this is [A-Za-z0-9_]; in Unicode mode it also matches letters and digits from other scripts, and the underscore.

Q

What does the shorthand <code>\s</code> match?

Any whitespace character: space, tab (\t), newline (\n), carriage return (\r), form feed (\f), and vertical tab (\v). In Unicode mode it can also match rare whi...