What Greedy and Lazy Mean
When a regex engine encounters a quantifier, it must decide how much of the input to consume. The greedy quantifier consumes as much as it can while still allowing the overall pattern to match. The lazy quantifier consumes as little as it can while still allowing the overall pattern to match.
Consider the pattern a* applied to the string aaa. The greedy version matches all three characters, producing aaa. The lazy version matches only the first, producing a. Both are valid matches. The difference is how much the quantifier claims.
This distinction matters most when the quantifier is followed by something that must also match. The greedy version will consume characters that the following part of the pattern could have consumed, forcing the engine to backtrack. The lazy version stops early, giving the following part a chance to match what it needs.
Common Quantifiers and Their Behavior
The standard quantifiers behave the same way whether greedy or lazy. The asterisk * means zero or more. The plus + means one or more. The question mark ? means zero or one. The curly braces {n,m} mean between n and m occurrences.
Each of these has a greedy and a lazy form. The greedy form is the default. The lazy form is written by adding a second question mark: *?, +?, ??, {n,m}?.
The greedy quantifier tries to match as many repetitions as possible. If the rest of the pattern fails, the engine backtracks, giving up one repetition at a time, until the pattern succeeds or the quantifier has nothing left to give. The lazy quantifier tries to match as few repetitions as possible. If the rest of the pattern fails, the engine advances, adding one repetition at a time, until the pattern succeeds or the quantifier has consumed all available input.
A Practical Example
Consider the pattern "(.*?)" applied to the string "foo" "bar". The lazy quantifier .*? matches the shortest possible string between the quotes, producing foo. The greedy version .* matches the longest possible string, producing foo" "bar.
This is the mistake that keeps appearing in production code. A developer writes "(.*)" expecting to extract the content of one quoted field, but the greedy quantifier swallows everything up to the last quote in the string. The result is a single match containing everything between the first and last quote, rather than the expected per-field extraction.
The fix is straightforward: use the lazy form "(.*?)" when you want the shortest match. Or, if you know the content of the field does not contain quotes, use "([^"]*)", which matches anything except a quote character. This is often more efficient because it avoids backtracking entirely.
When Greedy Is Actually Better
Greedy quantifiers are not always the problem. When you want the longest match, greedy is the right choice. Consider the pattern (\d+) applied to 12345. The greedy \d+ matches all five digits. The lazy \d+? matches only the first.
A common error is using lazy when greedy is needed. The pattern (.*?) applied to foo matches foo, but it does so by first matching zero characters, then backtracking one at a time until the rest of the pattern succeeds. With (.*), the greedy version matches all five characters in a single pass. For long strings, the greedy version can be significantly faster because it avoids the repeated backtracking.
The lazy quantifier is not inherently slower. It is only slower when the rest of the pattern forces the engine to repeatedly add characters and then remove them. If the rest of the pattern succeeds on the first attempt, lazy and greedy perform similarly.
Backtracking and Performance
Backtracking is the mechanism by which the engine resolves ambiguity. When a greedy quantifier has consumed too much, the engine backtracks by giving up characters one at a time. When a lazy quantifier has consumed too little, the engine advances by adding characters one at a time.
The cost of backtracking depends on the complexity of the rest of the pattern. Simple patterns like (.*)" backtrack efficiently because each step is cheap. Complex patterns with nested groups, alternations, or lookaheads can make backtracking expensive.
A well-known example is the pattern (a+)+b applied to a string of many a characters followed by a b. The greedy version can take exponential time in the worst case because the engine tries every possible way to distribute the a characters between the inner and outer groups. The lazy version avoids this particular problem but may still backtrack through the same number of states.
Writing Patterns That Work
The most reliable approach is to write patterns that match what you expect, then verify them against representative input. A pattern that works on a single test case may fail on a longer string. A pattern that works on short strings may fail on long ones.
Consider the pattern (\w+) applied to hello_world. The word character class includes underscores, so the greedy match is hello_world. If you want only the first word, use (\w+) with a non-word character boundary: (\w+)(?=\W|$).
The pattern (\w+) applied to hello-world matches hello, because the hyphen is not a word character. This is correct behavior, but it is easy to miss when the pattern is embedded in a larger expression.
Summary
Greedy quantifiers match as much as they can. Lazy quantifiers match as little as they can. The choice between them depends on what you want to match and what follows the quantifier in your pattern. Use lazy when you want the shortest match. Use greedy when you want the longest match or when the rest of the pattern can succeed on the first attempt. Verify your patterns against representative input, and use tools like RegexBuilder to check that the matches are what you expect.
Tool mentioned: RegexBuilder