ZetCode

Perl qr//

last modified October 20, 2026

The qr// operator quotes and compiles a regular expression and returns it as an object. Instead of repeating long patterns, you give them names, combine them, and pass them around like any other value. This turns complex regular expressions into readable building blocks.

Creating a compiled regex

The qr// operator compiles a pattern into a regex object without matching anything. The object can be stored in a variable, passed to subroutines and used wherever a pattern is expected.

basic.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

my $digits = qr/\d+/;

say "ref type: ", ref $digits;
say "stringified: $digits";

for my $s ('abc 123', 'no numbers') {
    if ($s =~ $digits) {
        say "'$s' matches, found: $&";
    } else {
        say "'$s' does not match";
    }
}

my $ci = qr/perl/i;
say "case-insensitive: ", ('I love PERL' =~ $ci ? 'yes' : 'no');

The type of the object, as reported by ref, is Regexp. Printing it shows the pattern together with its flags, in the form (?^u:...): the caret resets the flags to the defaults and the u comes from use v5.34, which enables Unicode string semantics. The object can be used directly on the right side of =~. Flags given to qr, such as /i, are stored in the object.

$ perl basic.pl
ref type: Regexp
stringified: (?^u:\d+)
'abc 123' matches, found: 123
'no numbers' does not match
case-insensitive: yes

Composing patterns

Regex objects can be interpolated into other patterns. Each one keeps its own flags, so small named pieces can be combined into a larger expression that stays readable.

compose.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

my $word = qr/[a-z]+/i;
my $num  = qr/\d+/;

my $line = qr/^($word)\s*=\s*($num)$/;

for my $s ('width = 80', 'Height=25', 'depth = x') {
    if (my ($k, $v) = $s =~ $line) {
        say "$k => $v";
    } else {
        say "no match: $s";
    }
}

my $with_flag = qr/hello/i;
say "embedded flags: ", qr/$with_flag world/;
say "'HELLO world' matches: ", ('HELLO world' =~ /^$with_flag world$/ ? 'yes' : 'no');
say "'HELLO WORLD' matches: ", ('HELLO WORLD' =~ /^$with_flag world$/ ? 'yes' : 'no');

The $line pattern is built from $word and $num, and the captured groups are returned in list context. The last lines show that the embedded flags travel with the object: the interpolated $with_flag keeps /i for the word hello, but not for the literal text outside it, so HELLO WORLD fails.

$ perl compose.pl
width => 80
Height => 25
no match: depth = x
embedded flags: (?^u:(?^ui:hello) world)
'HELLO world' matches: yes
'HELLO WORLD' matches: no

Building a larger pattern: IPv4

Composition pays off with complicated patterns. An IPv4 address is made of four decimal octets, and defining one octet once avoids repeating the long alternation.

ipv4.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

my $octet = qr/(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)/;
my $ipv4  = qr/^$octet(?:\.$octet){3}$/;

for my $ip ('192.168.1.10', '255.255.255.255', '256.1.1.1', '1.2.3', '10.0.0.01') {
    printf "%-16s %s\n", $ip, $ip =~ $ipv4 ? 'valid' : 'invalid';
}

The $octet pattern accepts 0-255 through four alternatives, and non-capturing groups (?:...) keep it from creating captures. The address pattern repeats the octet three more times after a dot. The out-of-range value and the incomplete address are rejected, and so is the octet with a leading zero.

$ perl ipv4.pl
192.168.1.10     valid
255.255.255.255  valid
256.1.1.1        invalid
1.2.3            invalid
10.0.0.01        invalid

Patterns from word lists

A list of words can be turned into an alternation. Every word must go through quotemeta to neutralize special characters, and sorting by length makes the longest word win.

words.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

my @keywords = ('if', 'else', 'while', 'for.each');

my $alt = join '|', map { quotemeta } sort { length $b <=> length $a } @keywords;
my $re  = qr/\b(?:$alt)\b/;

say "pattern: $re";

my $code = 'if x then for.each y else while z; foreach';
my @found = $code =~ /($re)/g;
say "found: @found";

(my $marked = $code) =~ s/($re)/[$1]/g;
say $marked;

The join call produces the alternation, and qr wraps it in word boundaries. The same object is used for a global match in list context and in a substitution. The text foreach is not marked, since it is not an exact keyword. The dot in for.each is matched literally because of quotemeta.

$ perl words.pl
pattern: (?^u:\b(?:for\.each|while|else|if)\b)
found: if for.each else while
[if] x then [for.each] y [else] [while] z; foreach

Tables of patterns

Regex objects are ordinary scalars, so they can be kept in arrays and hashes. A table of patterns is a clean replacement for a long chain of if and elsif statements.

table.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

my @rules = (
    [ integer => qr/^[+-]?\d+$/            ],
    [ float   => qr/^[+-]?\d*\.\d+(?:[eE][+-]?\d+)?$/ ],
    [ word    => qr/^\w+$/                 ],
    [ blank   => qr/^\s*$/                 ],
);

for my $input ('42', '-3.14', '.5e-2', 'hello', '  ', 'a b') {
    my ($type) = map { $_->[0] } grep { $input =~ $_->[1] } @rules;
    say sprintf "%-8s => %s", "'$input'", $type // 'unknown';
}

Each rule holds a name and a compiled pattern. The grep keeps the rules that match the input and map extracts the name; the first one is taken. The order matters here, since the word pattern would also accept digits, and the integer rule has to come first. Inputs matched by no rule are reported as unknown.

$ perl table.pl
'42'     => integer
'-3.14'  => float
'.5e-2'  => float
'hello'  => word
'  '     => blank
'a b'    => unknown

Reusing a regex

A regex object works with split, grep, s/// and hash storage alike. Compiling once and reusing it also avoids recompiling inside loops.

reuse.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

my $sep = qr/\s*[,;]\s*/;

my @fields = split $sep, 'a , b;c ;d';
say join '|', @fields;

my @items = grep { $_ =~ qr/^\d+$/ } qw(10 abc 20 3x 30);
say "@items";

my $ws = qr/\s+/;
(my $clean = "too   much \t space") =~ s/$ws/ /g;
say $clean;

my %compiled = map { $_ => qr/\b\Q$_\E\b/i } qw(perl ruby go);
for my $text ('I use Perl and Go', 'Ruby rocks') {
    my @hits = grep { $text =~ $compiled{$_} } sort keys %compiled;
    say "'$text' mentions: @hits";
}

The same style of object serves as separator in split, as filter in grep, and as pattern in a substitution. The last example precompiles one case-insensitive pattern per language into a hash; \Q...\E quotes any special characters in the key, and the loop then only looks the patterns up.

$ perl reuse.pl
a|b|c|d
10 20 30
too much space
'I use Perl and Go' mentions: go perl
'Ruby rocks' mentions: ruby

Best Practices

In this article we have covered the qr// operator: creating regex objects, composing larger patterns, building alternations from word lists, tables of patterns, and reusing a compiled regex.

Source

perlop: Regexp Quote-Like Operators

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.