Lazy vs Greedy Regex Quantifiers: Why Your Pattern Matches Too Much
Your regex grabbed the whole page instead of one tag. That's greedy matching by default. Here's how lazy quantifiers fix it — and the trade-off nobody mentions.
You need to pull every <p> tag out of a messy HTML export. You write the pattern <p>(.*)</p>, test it, and the match swallows the whole page — from the first opening tag to the last closing one. That's not a bug in the pattern. That's how greedy quantifiers work by default. The regex tester makes the behavior visible, and a lazy quantifier fixes it.
Why Greedy Quantifiers Overrun
In most regex engines, .* and .+ match as much as they can and only give characters back when the rest of the pattern demands it. Between the first <p> and the last </p>, every tag in between is "available," so the greedy match takes all of it. It's correct behavior — the engine returns the longest possible match — but it's rarely what you want when you're extracting structure.
The classic fix is a lazy quantifier: .*?. The question mark tells the engine to match as little as possible, expanding only until the next part of the pattern fits. <p>(.*?)</p> stops at the first </p>, so each <p> tag gets its own capture. Paste both patterns into the regex tester side by side and the difference is obvious in the match highlights.
The Counter-Intuitive Trade-off
Here's the part that trips up people who just learned lazy matching: lazy isn't always faster. A lazy pattern that fails has to try again at every single position, and patterns with nested quantifiers can collapse into catastrophic backtracking — the regex that "takes a second" runs for minutes. The real fix for the HTML problem is being specific about what you match: <p[^>]*>(.*?)</p> refuses to let the opening tag leak.
When you're debugging a pattern, don't guess at what the engine is doing. Run the failing match through the regex tester to see the actual span, compare the captured groups with the text diff tool, and if the expression is living inside code, drop it into the code formatter so it's readable before you ship it.
Greedy vs lazy isn't a style preference — it's the difference between matching one paragraph and matching the entire document. Once you can predict which one the engine will pick, you stop fighting your own regexes. For the reverse case, extracting only the text that matches, our guide to extracting and parsing text with regex covers the broader workflow. Test your next pattern in the regex tester before it ships.
Tools mentioned in this article
Regex Tester & Converter
Test regular expressions, replace with regex, extract matches, and generate code snippets for 10 programming languages.
Text Diff Checker
Compare two pieces of text side by side and see exactly what changed. Highlights additions, deletions, and modifications. Drop in old and new versions to spot edits.
Code Formatter
Format and beautify JSON, JavaScript, CSS, and HTML code. Make minified or messy code readable with one click. Supports 4 languages.
