ZetCode

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.

greedy.pl
#!/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.

counted.pl
#!/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.

quoted.pl
#!/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.

possessive.pl
#!/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.

backtrack.pl
#!/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

In this article we have covered greedy, lazy and possessive quantifiers, counted repetition, atomic groups, and the cost of backtracking.

Source

perlre: Quantifiers

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Perl tutorials.