I was testing some regular expressions, when I came across some amusing behavior of a regex when compiled with the qr// operator.

Synopsis

my $rx = 'abc'; my $qr = qr/$rx/; if ('ABC' =~ /$qr/i) { print "ABC matches /abc/i\n" } else { print "ABC does not match /abc/i\n" }

If I use the /i modifier, the regex is supposed to match in a case insentitive mode, i.e. "ABC" =~ /abc/i returns a match. However, if I compile the pattern with qr//, the result is different.

This is intriguing, and I have eventually found out why it happens, but before telling you, I would like to show some more examples and let you meditate on what may be happening behind the scenes.

More food for thought

This script shows some variations on the same tune. First a pattern that is applied in case insensitive mode won't match when we would expect it to. Then a pattern in dot-matches-all mode does not match a newline character.

However, when I use a literal pattern instead of a pre-compiled one, it matches.

#!/usr/bin/perl -w use strict; my @patterns = ('abc', 'xyz'); my %regexes = map { $_, qr/$_/} @patterns; my @strings = ('the alphabet starts with ABC', 'the alphabet ends with XYZ' ); for my $str(@strings) { for ( keys %regexes ) { print qq("$str" =~ /$_/i => ); if ($str =~ /$regexes{$_}/i) { print "(qr) match\n"; } else { print "(qr) no match\n" } } } for my $str(@strings) { for ( @patterns ) { print qq("$str" =~ /$_/i => ); if ($str =~ /$_/i) { print "(pattern) match\n"; } else { print "(pattern) no match\n" } } } my $string = <<END; This text spawns across multiple lines END my $pattern = 'multiple .+ lines'; my $regex = qr/$pattern/x; if ($string =~ /$regex/s) { print "dot-matches-all (qr) matches\n"; } else { print "dot-matches-all (qr) does not match\n"; } if ($string =~ /$pattern/xs) { print "dot-matches-all (literal) matches\n"; } else { print "dot-matches-all (literal) does not match\n"; } __END__ "the alphabet starts with ABC" =~ /abc/i => (qr) no match "the alphabet starts with ABC" =~ /xyz/i => (qr) no match "the alphabet ends with XYZ" =~ /abc/i => (qr) no match "the alphabet ends with XYZ" =~ /xyz/i => (qr) no match "the alphabet starts with ABC" =~ /abc/i => (pattern) match "the alphabet starts with ABC" =~ /xyz/i => (pattern) no match "the alphabet ends with XYZ" =~ /abc/i => (pattern) no match "the alphabet ends with XYZ" =~ /xyz/i => (pattern) match dot-matches-all (qr) does not match dot-matches-all (literal) matches

It is puzzling, isnt'it?

OK. Enough suspense. Let's solve the mistery.

Why?

The reason for this behavior is that qr// will compile the pattern with the modifiers we specify at its end. For example, qr/perl/i will happily match "Perl", "perl", and "PERL." The interesting thing that is silently happening, though, is that qr// is setting the /x, /m and /s modifiers as well. If we mention them explicitly, they are operational, if we don't, they are set as non operational. Let's ask Perl itself to unveil the truth.

$ perl -e 'for (qw( i x s m )) {print eval "qr/perl/$_", "\n"}' (?i-xsm:perl) (?x-ism:perl) (?s-xim:perl) (?m-xis:perl)

As you can see, each pattern is compiled as if we had inserted a (?y-z:) block inside a regular expression. For those who don't recall it, such block allows the insertion of a sub-expression with modifiers that only apply within the block's boundaries. Thus, we can insert a case sensitive sub expression within a case insensitive regex. Each modifier following the question mark is set. The ones prepended by a minus sign are unset.

Looking at the outcome of the latest example, we can see that for each modifier that we set explicitly, qr// will implicitly unset the others.

Coming back to our main example, the values in %regexes are (?-xism:abc) and (?-xism:xyz). Keeping in mind the above explanation for sub-expressions, it is clear that this pre-compilation with qr// can't match those patterns. The same is true for the "dot-matches-all" modifier. A pattern compiled with qr//x will end up with (?x-ism:pattern) and even though it is later embedded in a regex with the /s modifier, its matching benefits can't kick in.

Further reading

perlre and perlop are vague about this issue. The only place I've found it mentioned and explained in plain English is Mastering Regular Expressions, 2nd Ed.

Update
Changed title upon Aristotle's suggestion. (Was qr// hidden risks)

 _  _ _  _  
(_|| | |(_|><
 _|   

In reply to Risks in the oblivious use of qr// by gmax

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.