Hash inside \Q and \E

Viewed 76

I would like to match a pattern with perl that contains a hash (or pound character '#'):

#!/usr/bin/perl

use strict;
use warnings;
use utf8;

if( $#ARGV + 1 != 2 ) {
    die "ERROR: Number of command line arguments is not 2";
}
my $input = $ARGV[ 0 ];
my $output = $ARGV[ 1 ];

open( my $ifh, "<:encoding(UTF-8):crlf", "$input" )
    || die "ERROR: Failed to open input file: $!";
my $content = do { local $/; <$ifh> };

open( my $ofh, ">:encoding(UTF-8):crlf", "$output" )
    || die "ERROR: Failed to open output file: $!";

eval { # GLOBAL EXCEPTION HANDLER BEGIN

{ ( ( my $fix, my $dat, my $rest ) = ( $content
=~ / ^ ( \Q# started on \E ) ([^\n]+) \n{3}
       (.*)
     $ /xs
) ) || die "ERROR: No match";
print $ofh __LINE__ . "\tDate\t$dat\n";
$content = $rest; }

}; if( $@ ) { print $ofh "$@____________\n"; print $ofh "$content"; } # GLOBAL EXCEPTION HANDLER END

This gives the following error message:

Unmatched ( in regex; marked by <-- HERE in m/ ^ (  <-- HERE \#\ started\ on\ \\E\ \)\ \(\[\^\\n\]\+\)\ \\n\{3\}\
\ \ \ \ \ \ \ \(\.\*\)\
\ \ \ \ \ \$\ / at ./example.pl line 25.

However, if I change it like this, then it works perfectly:

{ ( ( my $fix, my $dat, my $rest ) = ( $content
=~ / ^ ( \# \Q started on \E ) ([^\n]+) \n{3}
       (.*)
     $ /xs
) ) || die "ERROR: No match";
print $ofh __LINE__ . "\tDate\t$dat\n";
$content = $rest; }

I would prefer, however, to keep the hashtag inside the \Q and \E block.

So the question is, does \Q and \E not escape everything inside it? Or what is wrong here?

1 Answers

# inside a regex with /x introduces a comment. You need to backslash it even inside \Q...\E under /x.

BTW, the part inside eval can be made a bit more readable by using or instead of || for flow control (it's a recommended practice, anyway). Also, don't relay on $@ to be set after an error, there were bugs in older Perl versions that caused it to not be set properly. Instead, return a true value from the eval and check it, e.g.

{
    ( my $fix, my $dat, my $rest )
        = $content =~ / ^ ( \Q \# started on \E ) ([^\n]+) \n{3}
                        (.*)
                        $
                      /xs or die "ERROR: No match";
1 } or do { # Handle the exception...

Update: It's a bug in Perl. Note that it hits only when an octothorpe is used literally, i.e. you can store it in a variable and it will work:

my $octothorpe = '#';
print 'a # b' =~ /(\Q $octothorpe \E) b/x
Related