\Q acts differently in s/// and m// operations

G

gargoyle

Consider the following code:

#!/usr/bin/perl -w
# NOTE: don't focus on the backslashes, I already know Win32 can
# use / instead. This is simply a regex+metachar question...

$old = 'C:\\test\\1\\'; # old parent dir
$new = 'C:\\test\\2\\'; # new parent dir
$path = 'C:\\test\\1\\bin\\'; # the path we're renaming
warn "old=$old, new=$new, path=$path\n";

warn "----- doing match! -----\n";
$path =~ /^(\Q$old\E)/; # this matches
warn "captured \$1 = $1\n";
$path =~ /^(\Q$new\E)/; # this doesn't
warn "captured \$1 = $1\n";
warn "old=$old, new=$new, path=$path\n";

warn "----- doing substitution! -----\n";
$path =~ s/^\Q$old\E/\Q$new\E/;
warn "old=$old, new=$new, path=$path\n";
__END__

The output:

old=C:\test\1\, new=C:\test\2\, path=C:\test\1\bin\
----- doing match! -----
captured $1 = C:\test\1\
captured $1 = C:\test\1\
old=C:\test\1\, new=C:\test\2\, path=C:\test\1\bin\
----- doing substitution! -----
old=C:\test\1\, new=C:\test\2\, path=C\:\\test\\2\\bin\

In the m// operation, the captured output is the original variable, not
the quotemeta version.

But in the s/// operation, the output is quoted.

Why are they behaving differently? And is there a way to get unquoted
output from the s/// operation, or am I just better off using substr(),
since after all there's no unquotemeta() function?
 
B

Bob Walton

gargoyle wrote:

....
In the m// operation, the captured output is the original variable, not
the quotemeta version.

But in the s/// operation, the output is quoted.

Why are they behaving differently? And is there a way to get unquoted
output from the s/// operation, or am I just better off using substr(),
since after all there's no unquotemeta() function?

Well, the "replacement" in the s/// operator is a *string*, not a
regexp. And that string is processed per qq(). For that, \Q has
a different meaning -- all non-word characters following \Q are
quoted, just as you noted above. In the m// regexp, it is the
*regexp* characters that are quoted, and not any characters in
the matched string. You probably should simply dispense with the
\Q and \E in your replacement string. For details, see:

perldoc perlop

particularly the sections on quote and quote-like operators and
on regexp quote-like operators.

And there *is* an "unquotemeta" function for a string quoted by
\Q -- eval(qq(qq/$string/)) should do the trick, I think, as in:

d:\junk>perl -e "$a=qq(ab\Q~!@#\E~!@#);print eval qq/qq($a)/"
ab~!@#~!@#
d:\junk>
 
G

gargoyle

Well, the "replacement" in the s/// operator is a *string*, not a
regexp. And that string is processed per qq(). For that, \Q has
a different meaning -- all non-word characters following \Q are
quoted, just as you noted above.

Aha! Now I understand. Thanks for clearing that up.
And there *is* an "unquotemeta" function for a string quoted by
\Q -- eval(qq(qq/$string/)) should do the trick, I think, as in:

d:\junk>perl -e "$a=qq(ab\Q~!@#\E~!@#);print eval qq/qq($a)/"
ab~!@#~!@#
d:\junk>

Interesting... But I have trouble understanding the impact of several
levels of quoting and interpolation. It's late though, so I'll re-read
the perlop manpage tomorrow and see if I can make sense of it.
 
I

Ilya Zakharevich

[A complimentary Cc of this posting was sent to
Bob Walton
Well, the "replacement" in the s/// operator is a *string*, not a
regexp. And that string is processed per qq().
Correct.

For that, \Q has
a different meaning

Wrong. \Q has the same meaning everywhere.
-- all non-word characters following \Q are quoted

That's it.
In the m// regexp, it is the *regexp* characters that are quoted,
and not any characters in the matched string.

Wrong. But since quoting some characters is a NOOP (e.g., \: matches
the same as : ), the semantic is somewhat similar to what you wrote -
which is probably the reason for your confusion.
You probably should simply dispense with the \Q and \E in your
replacement string.

Without reading the mind of the author, giving any advice does not
make sense. I would just note that

s(REX)(\Q$var);

is the same as

$repl = quotemeta $var;
s(REX)($repl);

(but the first version is much harder to debug).

Hope this helps,
Ilya
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,733
Messages
2,569,440
Members
44,829
Latest member
PIXThurman

Latest Threads

Top