matching string literals

Do you have a question? Post it now! No Registration Necessary.  Now with pictures!

Threaded View

I would like to able to match strings with several uninterpreted
characters (for instance, "/", "-", "(", and "=").

Ideally, I would like to not have to escape each of these characters
with a "\", as I have many different strings with many special
characters to try to match.

Is there any way to force the search of a string literally?

I have tried m/\Q $string \E/, but the issue is that $string can
contain "/", which perl interprets rather than taking it literally.

Thank you in advance.

Re: matching string literals

Quoted text here. Click to load it

Try m{\Q $string \E}.
As a side note, $string has spaces around it that is literal in the
regex unless m//x


Re: matching string literals

On Tue, 01 Feb 2011 15:57:04 -0800, wrote:

Quoted text here. Click to load it

To correct myself, '/' has no special meaning inside of regex's,
its taken as a literal.

The problem is that as you see it, '/' is being used as a delimeter
that Perl, when you say m//, uses to parse out the regex.
Either s/// or m//. Any character can be used as the delimeter.


 m/ $string /       parses out ' $string '
 m# $string /#      parses out ' $string /'
 m/ $string/ /      syntax error, one too many delimeters '/'

The regex is parsed, variable interpolation is done,
then the regex is evaluated for proper syntax.
But, it is parsed first. So any character in $string used as a
delimeter before parsing is not considered a parsing character
because parsing is already done.

You can use different characters for the delimeter, but some
delimeter characters have special meaning to the parser.
See perlop manpage for m// and also quote like operators.

quotemeta EXPR:

This documentation (perlfunc man page), says:
"Returns the value of EXPR with all non-"word" characters backslashed."
e.g. NOT /[A-Za-z_0-9]/

Why do they do this? To take away special escape characters and
the possibility of quantifier construct-punctuation.

Unfortunately, they ruin the entire string if you actually want
any of these (especially the escape character) in there. The net result
is that everything in the string is innoculated, but this throws
out the baby with the bathwater.

The best thing to do is learn what the perl special characters are,
ie: its metacharacters, then do your own escaping where needed.


Quotemeta does this:
      (my $tmp_str = $string) =~ s/(^\W)/\$1/g;

Since quotemeta() escapes all non-word characters, you could exclude
some chararacters from being escaped with a negative class.

For instance [^\W\] will escaped all non-words but won't escape
'' itself. So, you could run your $string through this:
   (my $tmp_str = $string) =~ s/([^\W\])/\$1/g;
Add any other characters to the negative class you don't want to be escaped.

You could also create a custom character property class using the
\p{} or \P{} construct. In that class you could just define the metachars
then use that to escape just those characters.

    (my $tmp_str = $string) =~ s/(\p)/\$1/g;

or possibly without the '' escape character itself,
     (my $tmp_str = $string) =~ s/([^\P\])/\$1/g;$string =~ /

More often then not, if searching for a literal '\n' or other literal
control characters, the '' is not desired to be escaped.


Here is some code you could test out that illustrates these options...
Wether or not the \p{} construct invokes some mega unicode database
I'm not sure of, or if there is a performance hit. If it is, its just
a one time event.


use warnings;
use strict;

sub InMeta {
 # {}[]()^$.|*+?\
   return <<END;

my $rx = q!{}[]()^$.|*+?\<--meta, word-->ABCabc123_, newline \n!;

# Compressed:
# (my $rx_quoted_meta1 = $rx) =~ s/(\p)/\$1/g;
# (my $rx_quoted_meta2 = $rx) =~ s/([^\P\])/\$1/g;
# (my $rx_quoted_all1 = $rx)  =~ s/(\W)/\$1/g;
# (my $rx_quoted_all2 = $rx)  =~ s/([^\w\])/\$1/g;

(my $rx_quoted_meta1 = $rx) =~ s/
        \p  # Custom meta class

(my $rx_quoted_meta2 = $rx) =~ s/
        [^           # Negative class
          \P   # not meta class (result = include meta chars)
          \           # '' escapes (exclude)

(my $rx_quoted_all1 = $rx) =~ s/(\W)/\$1/xg;

(my $rx_quoted_all2 = $rx) =~ s/
        [^          # Negative class
           \w         # words (exclude)
           \         # '' escapes (exclude)

my $rx_Q = quotemeta $rx;

print "Original:\n$rx\n\n";

print "Quoted meta:\n$rx_quoted_meta1\n\n";
print "** Quoted meta, not \:\n$rx_quoted_meta2\n\n";
print "Quoted all, not words:\n$rx_quoted_all1\n\n";
print "Quotemeta function:\n$rx_Q\n\n";
print "Quoted all, not words, not \:\n$rx_quoted_all2\n\n";


{}[]()^$.|*+?\<--meta, word-->ABCabc123_, newline \n

Quoted meta:
\\[\]\(\)\^$\.\|\*\+\?\<--meta, word-->ABCabc123_, newline \n

** Quoted meta, not \:
\\[\]\(\)\^$\.\|\*\+\?\<--meta, word-->ABCabc123_, newline \n

Quoted all, not words:
\\[\]\(\)\^$\.\|\*\+\?\<\-\-meta\,\ word\-\-\>ABCabc123_\,\ newline\ \n

Quotemeta function:
\\[\]\(\)\^$\.\|\*\+\?\<\-\-meta\,\ word\-\-\>ABCabc123_\,\ newline\ \n

Quoted all, not words, not \:
\\[\]\(\)\^$\.\|\*\+\?\<\-\-meta\, word\-\-\>ABCabc123_\, newline \n

Re: matching string literals

On Wed, 02 Feb 2011 12:50:33 -0800, wrote:

Quoted text here. Click to load it
      (my $tmp_str = $string) =~ s/(\W)/\$1/g;
      (my $tmp_str = $string) =~ s/([^\w])/\$1/g;



Re: matching string literals

Quoted text here. Click to load it

There is standard function that probably does exactly what you are
asking for, see
    perldoc -f index


Re: matching string literals

Quoted text here. Click to load it

3 of those 4 are not special in regular expressions.

Quoted text here. Click to load it

You don't have to, as only "(" would require a backslash in the regex.

Quoted text here. Click to load it

Yes, by using \Q

Quoted text here. Click to load it

No it doesn't.

If you post real code that we can run that displays the problem
you are having, then we can help you fix it.

If you don't, we can't.

This code matches 4 times...

use warnings;
use strict;

$_ = 'foo / bar = baz - frob ( nitz';

my $string = '/';
print "matched [$string]\n" if m/ $string /;

$string = '=';
print "matched [$string]\n" if m/ $string /;

$string = '-';
print "matched [$string]\n" if m/ $string /;

$string = '(';
print "matched [$string]\n" if m/\Q $string \E/;

Tad McClellan
email: perl -le "print scalar reverse qq/moc.liamg0cm.j.dat/"
The above message is a Usenet post.
I don't recall having given anyone permission to use it on a Web site.

Site Timeline