Perl regex quantifiers
last modified October 20, 2026
Quantifiers say how many times the preceding item may repeat. Their behaviour decides whether a pattern grabs too much, too little, or takes a very long time. This article compares the greedy, lazy and possessive forms and shows how backtracking works.
Greedy and lazy quantifiers
Quantifiers such as *, + and ? are greedy
by default: they match as much as possible and give back characters only if the
rest of the pattern fails. A question mark after the quantifier makes it lazy,
so it matches as little as possible.
#!/usr/bin/perl use strict; use warnings; use v5.34.0; my $html = '<b>bold</b> and <b>more bold</b>'; my ($greedy) = $html =~ /<b>(.*)<\/b>/; say "greedy: $greedy"; my ($lazy) = $html =~ /<b>(.*?)<\/b>/; say "lazy: $lazy"; my @all = $html =~ /<b>(.*?)<\/b>/g; say "all lazy: ", join ' | ', @all; my ($class) = $html =~ /<b>([^<]*)<\/b>/; say "negated class: $class";
The greedy .* runs to the last closing tag and swallows everything
in between. The lazy .*? stops at the first one, and with
/g it finds both elements. A negated character class such as
[^<]* reaches the same result and is usually faster, since it
never has to backtrack.
$ perl greedy.pl greedy: bold</b> and <b>more bold lazy: bold all lazy: bold | more bold negated class: bold
Counted repetition
Braces specify exact ranges: {n} exactly n times, {n,}
at least n, and {n,m} between n and m. Each form has a lazy variant
with a trailing question mark.
#!/usr/bin/perl
use strict;
use warnings;
use v5.34.0;
my $s = 'aaaaa';
for my $re (qr/^(a{2,4})/, qr/^(a{2,4}?)/, qr/^(a{3})/, qr/^(a{2,})/) {
my ($m) = $s =~ $re;
say sprintf "%-16s matched '%s'", $re, $m // 'undef';
}
my $phone = '555-1234';
say "exact repeat: ", ($phone =~ /^\d{3}-\d{4}$/ ? 'valid' : 'invalid');
say "optional part: ", join ',', map { /^\d+(?:\.\d+)?$/ ? 'ok' : 'bad' } qw(3 3.14 3. .5);
Given five letters, {2,4} takes four, while the lazy
{2,4}? takes only the minimum of two. The open range
{2,} takes everything. The stringified patterns show the flags
added by qr. The last lines validate a fixed format and an optional
fractional part.
$ perl counted.pl
(?^u:^(a{2,4})) matched 'aaaa'
(?^u:^(a{2,4}?)) matched 'aa'
(?^u:^(a{3})) matched 'aaa'
(?^u:^(a{2,})) matched 'aaaaa'
exact repeat: valid
optional part: ok,ok,bad,bad
Quoted strings and escapes
Quoted strings are the classic case where greedy and lazy quantifiers give different answers. A pattern that also handles escaped quotes needs an alternation.
#!/usr/bin/perl use strict; use warnings; use v5.34.0; my $line = 'name="Alice" city="Paris" note="say \"hi\""'; my @naive = $line =~ /"(.*)"/; say "greedy: @naive"; my @pairs = $line =~ /(\w+)="(.*?)"/g; say "lazy: @pairs"; my @proper = $line =~ /(\w+)="((?:[^"\\]|\\.)*)"/g; say "escapes: ", join ' | ', @proper;
The greedy version runs from the first quote to the last one on the line. The
lazy version separates the pairs but breaks at the escaped quote in the last
value. The robust pattern (?:[^"\\]|\\.)* consumes either a normal
character or a backslash with the character after it, so the escaped quote does
not end the string.
$ perl quoted.pl greedy: Alice" city="Paris" note="say \"hi\" lazy: name Alice city Paris note say \ escapes: name | Alice | city | Paris | note | say \"hi\"
Possessive quantifiers and atomic groups
A possessive quantifier, written with a trailing plus such as a++,
matches as much as it can and never gives anything back. An atomic group
(?>...) does the same for a whole subpattern: once it has
matched, Perl will not backtrack into it.
#!/usr/bin/perl use strict; use warnings; use v5.34.0; my $s = 'aaab'; say "backtracking a+ab: ", ($s =~ /^a+ab$/ ? 'match' : 'no match'); say "possessive a++ab: ", ($s =~ /^a++ab$/ ? 'match' : 'no match'); say "atomic (?>a+)ab: ", ($s =~ /^(?>a+)ab$/ ? 'match' : 'no match'); my $text = 'foo123bar'; say "possessive keeps digits: ", ($text =~ /^\D++(\d++)\D++$/ ? $1 : 'no match'); my $csv = '"one", "two", "three"'; my @items = $csv =~ /"([^"]*+)"/g; say "items: @items";
Plain a+ in ^a+ab$ gives back one letter so that the
following a can match. With a++ or
(?>a+) the letters are kept, the rest of the pattern cannot
match, and the whole match fails immediately. This is the right behaviour when
giving back would be wrong, or just wasted work. The last two examples use
possessive quantifiers to tokenize digits and quoted items.
$ perl possessive.pl backtracking a+ab: match possessive a++ab: no match atomic (?>a+)ab: no match possessive keeps digits: 123 items: one two three
Counting backtracking
Backtracking is the engine retrying alternatives after a failure. Patterns with nested quantifiers can try a very large number of ways to split the input. Perl avoids the worst cases with caching, but the cost can still grow quickly.
#!/usr/bin/perl
use strict;
use warnings;
use v5.34.0;
my $steps;
sub count_steps {
my ($re, $text) = @_;
$steps = 0;
$text =~ $re;
return $steps;
}
my $plain = qr/^(\w+(?{ $steps++ })\s?)*$/;
my $atomic = qr/^(?>(\w+(?{ $steps++ })\s?)*)$/;
my $possess = qr/^(\w++(?{ $steps++ })\s?+)*+$/;
say sprintf "%-4s %-8s %-8s %s", 'n', 'plain', 'atomic', 'possessive';
for my $n (4, 8, 12, 16, 20) {
my $text = ('a' x $n) . '!';
say sprintf "%-4d %-8d %-8d %d", $n,
count_steps($plain, $text),
count_steps($atomic, $text),
count_steps($possess, $text);
}
A code block (?{ ... }) increments a counter each time the inner
group is entered, which is a rough measure of work. The input is a run of
letters followed by an exclamation mark, so the whole match must fail. The plain
nested quantifier does more and more work as the input grows, while the atomic
and possessive versions need only one step. Use code blocks like this for
experiments only.
$ perl backtrack.pl n plain atomic possessive 4 13 1 1 8 43 1 1 12 88 1 1 16 151 1 1 20 228 1 1
Best Practices
- Prefer negated classes:
[^"]*is faster and clearer than.*?for delimited text. - Use lazy quantifiers for the shortest match: but check that the end marker is unambiguous.
- Use possessive forms when backing off is useless: they fail quickly.
- Avoid nested quantifiers:
(a+)+and similar forms invite trouble. - Anchor patterns: an anchored pattern fails faster than an unanchored one.
In this article we have covered greedy, lazy and possessive quantifiers, counted repetition, atomic groups, and the cost of backtracking.
Source
Author
List all Perl tutorials.