Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Single Quotes - how to avoid any escape processing?

by temporal (Pilgrim)
on Jan 31, 2013 at 19:37 UTC ( [id://1016381]=perlquestion: print w/replies, xml ) Need Help??

temporal has asked for the wisdom of the Perl Monks concerning the following question:

Hello again monks,

For a long time I thought single quotes took a string exactly as it was and ignored all escaping. Faster, better for hardcoded values, etc. It made sense! But when putting Windows paths into strings I noticed some odd behaviors. Usually I just chalk this up to an eccentricity of Perl and just workaround it but I figured the time has come to clarify. Better explained through code:

$foo = '\\some.address.or.whatever\subdir\'; print $foo;

And even my IDE's syntax highlighting picks this up - I've escaped that last single quote which makes sense, I guess. So this code creates a runaway string - but I thought single quotes don't do handle any escaped characters? Hm. Except the single quote. Fair enough, how else would you get a single quote character into a singly quoted string?

But then this, when I need double backslashes to indicate a network path:

$foo = '\\some.address.or.whatever\subdir\file'; print $foo;

Okay, so I didn't try to end on a backslash like last time so at least this snippit executes. But you'll notice that the double backslash gets escaped to a single backslash. Arg, that won't do. So usually I'll just go in and drop the extra backslashes in. But let's be real here - '\\\\' just looks godawful.

First I went to q{string_w_escapes}, but that does the same thing (understandably). But I was really hoping it would resolve that trialing backslash issue - which it doesn't. It just escapes the delimited character, so now I find that some non-alphanumeric escaped characters (ex: \{) are parsed in singly quoted strings? Double arg.

Then I remembered that Perl allows explicit string building using <<'here-doc'. I hate using heredocs - they're ugly and feel clunky. But let's give that a shot:

$foo = <<'PATH'; \\some.address.or.whatever\subdir\ PATH print "this is a test${foo}again\ntesting";

Okay, so I've single quoted the heredoc to skip any processing of escapes - which finally works. Hooray, look at those two backslashes! But wait, hold the phones... now there is this unavoidable newline character at the end of the string. Triple agh! Probably something to do with the nature of how heredoc tags are parsed. Sure wish it would just terminate the string with a nice "\0". Since I do not think it's possible specify a heredoc without that trailing newline I guess this option doesn't really work either (almost relieved, I would hate to define all my hardcoded paths as heredocs).

I just want the string as I've typed it, no matter what character(s) it contains. Seems like it could be useful if one had to define a large number of strictly typed strings: reduces resource overhead of processing each of them for escapes and whatnot. Or is this just the nature of Perl encoding all strings into its internal representation 'under the hood' and I'm going to have to live with it? I really thought there would be some builtin like ql{} (quote literal?) which would allow this kind string definition.

TLDR:
So is there a (simple) way to put a string into a scalar variable without any character escaping or processing of the contents of that string at all?

Replies are listed 'Best First'.
Re: Single Quotes - how to avoid any escape processing?
by ww (Archbishop) on Jan 31, 2013 at 19:53 UTC

    Quote-like operators ( http://perldoc.perl.org/perlop.html#Quote-and-Quote-like-Operators ) may be helpful:

    C:\>perl -E "use strict; use warnings; my $str=q(This isn't really an +answer for URLs like 'http:\\\\www.perlmonks.org!'); say $str;" This isn't really an answer for URLs like 'http:\\www.perlmonks.org!'
    ... or maybe this node is an answer, of sorts. Whadda'ya tink?
Re: Single Quotes - how to avoid any escape processing?
by LanX (Saint) on Jan 31, 2013 at 20:04 UTC
    Welcome to the world of escaping! :)

    If you have an end delimiter and you want to allow this end delimiter to appear literally you need to escape it, hence \' => '

    But if you're escaping, you're also needing an escape character, which has also to be escaped when used literally, hence \\ => \

    And thats it, nothing else needed within the single quotes family of q{} with a free choice of end delimiters.

    And now, if you think that perl sucks try emacs lisp, which only supports double quotes! In elisp regexes suffer from a ugly disease called slasheritis².

    Luckily you can use heredocs, because there is no delimiter that need to be escaped. And if you don't like the newlines then chomp:

    $foo = <<'__HERE__'; \\some.address.or.whatever\subdir\ __HERE__ chomp $foo; print ">", $foo ,"<"; # >\\some.address.or.whatever\subdir +\<

    I suppose you don't wanna put multiple path in a long heredoc? ¹

    So why don't you use a character like / as placeholder?

    sub s2b { # slash to backslash (my $str = shift) =~ tr#/#\\#; return $str; } my $path = s2b '//some.address.or.whatever/subdir/';

    Just don't blame Perl for M$ decision to use an escape character for separating paths. =)

    Cheers Rolf

    update

    fixed code for s2b()

    footnotes

    ¹) you could read them into a hash defined by preceded hash keys.

    use Data::Dump; sub parsepath { my $str=shift; return split /\s+/,$str; } my %path = parsepath <<'__HERE__'; install \\some.address.or.whatever\subdir\ deinstall \\other.address.or.whatever\subdir\ __HERE__ dd \%path;

    ==>

    { deinstall => "\\\\other.address.or.whatever\\subdir\\", install => "\\\\some.address.or.whatever\\subdir\\", }

    ²) yes, it insults the eyes!!!

      If you have an end delimiter and you want to allow this end delimiter to appear literally you need to escape it

      But why is this always considered a necessary feature for a quoting delimiter, trumping all other considerations like avoiding headaches like the OP describes?

      For a language that has so many different quoting delimiters as Perl does, I would have expected there to be at least one that would simply drop the pedantic "must allow each and every Unicode character to appear in the string literal" requirement and do no escaping at all.
      With a use-case like file paths, it's quite unlikely that not being able to include single quotes (or a similar special-char delimiter) would become a problem.

      In addition, I'm not convinced that introducing a separate escape character (in this case, backslash) is technically necessary for implementing single-quoted strings that are supposed to allow single-quotes to appear inside them.
      What would prevent the usage of the delimiting character as its own escape character? I.e. two consecutive single-quotes would not terminate a single-quoted string literal, they would be interpreted as one single-quote.
      I don't see any reason for ever wanting to put two string literals right next to each other without space or comma in between, so it would not create any syntactical ambiguity.
      Or am I missing something?

        > But why is this always considered a necessary feature for a quoting delimiter, trumping all other considerations like avoiding headaches like the OP describes?

        I didn't say it's a necessary, I explained the motivation "If ... you want to allow ...".

        > What would prevent the usage of the delimiting character as its own escape character? I.e. two consecutive single-quotes would not terminate a single-quoted string literal

        IIRC VB (or VB-Script) does something like this.

        > I don't see any reason for ever wanting to put two string literals right next to each other without space or comma in between, so it would not create any syntactical ambiguity.

        empty string?

        > Or am I missing something?

        Perl has already a very complex syntax, adding new extra DWIM exception doesn't make it easier.

        What might be easy and meaningful for you can be line noise or just complexity horror for someone else.

        For instance see this discussion about the differences between Perl and JS in handling the closing bracket in regex-character classes: Regex: Char-class equivalent for [^].

        So in short: Rules have to be kept as simple as possible and backslashing is a well-established notation.

        Moritz already mentioned a new way in Perl6, which sounds reasonably simple, so help is on the way for the next version.

        EDIT: And for the records: The OP might hate them but I LOVE HERE-DOCS!!!

        Cheers Rolf

Re: Single Quotes - how to avoid any escape processing?
by moritz (Cardinal) on Jan 31, 2013 at 20:14 UTC
    So is there a (simple) way to put a string into a scalar variable without any character escaping or processing of the contents of that string at all?

    Not that I know of.

    If you have large amounts of such data, maybe put them in a __DATA__ section, or into a config file.

    That said, Perl 6 has a quoting construct Q'...' that doesn't have any escape sequences. Of course you can't escape the closing delimiter then.

Re: Single Quotes - how to avoid any escape processing?
by tobyink (Canon) on Jan 31, 2013 at 21:06 UTC

    Although I released it as a joke module, Acme::What does the trick!

    use 5.010; use strict; use warnings; use Acme::What; sub WHAT { +shift } my $var = what \\foo; say $var;

    I wonder if I should create a slightly saner version along the lines of the Perl 6 Q operator that moritz mentioned?

    package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name
backslashes sometimes unnecessary
by soonix (Canon) on Jan 31, 2013 at 21:26 UTC

    This isn't perl specific, but I think it's helpful to know that many Windows APIs (including several Microsoft ones) do accept forward slashes as well.

    Try

    perl -e "print join ' ', glob 'C:/Windows/sys*'"
    and enjoy :-)

    Although this does not solve your original issue, perhaps it helps alleviate the problem at hand?

      IIRC all the old DOS APIs accepted "/" as path delimiters, it was just command.com itself that did not.

      package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name
Re: Single Quotes - how to avoid any escape processing?
by temporal (Pilgrim) on Jan 31, 2013 at 20:59 UTC

    Thanks for the replies guys.

    Yes, it would appear that (for the Googlers) this is not possible.
    At least not in any simple, straightforward method that I was hoping for.

    ww, I did check out that section of the PerlDocs - it told me about what I was expecting.
    However the reread led me to this (appropriately christened!) section of the same doc which definitively spells everything out about this case, and many others not even considered in this node:

    http://perldoc.perl.org/perlop.html#Gory-details-of-parsing-quoted-constructs

    This is one of the rare cases where Perl's DWIM philosophy doesn't align with my own, oh well.

    A (basic) note - the selected delimiter for q{} is always vulnerable and needful of and being escaped. As you'd expect.

    And thanks for the quality snippets LanX, that is probably what I'd end up doing if I had a long list. Fortunately, this time I was just curious about that case =)
    Hehe, I certainly don't blame Perl for MS monkey business. The scary part is that I'm starting to get used to it. Boy, sometimes I miss my last position where I worked primarily with Linux-based systems ;)

    Of course, moritz offers another good solution for larger volumes of data as well ♥

    So thanks again monks.
    Hopefully this node will stand as an answer, of sorts (heh, ww =D )

    Strange things are afoot at the Circle-K.

      Things get even worse when you windows path includes whitespace. (e.g. c:\Documents and Settings\.....). Windows requires quotes around the path. Those quotes must be part of the perl string. The section of documentation that you just cited has everything you need to know, but it is not easy.

      Bill

        Oh yeah, fun stuff. I think at that point I'd start using one of the path parsing/porting modules to handle it all.

        iirc File::Path and File::Basename intelligently handle paths with spaces on Windows.

Re: Single Quotes - how to avoid any escape processing?
by SteveDC (Initiate) on Aug 25, 2019 at 18:10 UTC
    Although this is an old thread I think it aligns well with a current issue I have whilst processing Windows paths. I have spent the last hour scouring THE WEB and can't for the life of me find a clean solution to my issue. Basically what I want to do is split a path into a list of each sub directory, so in its simplest form I tried the following...
    $PathOnly = "c:\Dir1\Dir2\Dir3"; @SubDirs = split('\', $PathOnly);
    This doesn't work since the \' gets escaped and I end up effectively with a missing ' So, tried to escape the \ with the following...
    @SubDirs = split('\\', $PathOnly);
    But that also has an error "trailing \ in regex" As does the following...
    @SubDirs = split("\\", $PathOnly);
    I even tried the following...
    my $SplitAt = "\\"; my @SubDirs = split($SplitAt, $PathOnly);
    Whilst this doesn't have syntax errors, and $SplitAt seems to get theexpected "\" it fails on the execution of the split with the same "trailing \ in regex", just at execution time this time. Now... just for kicks and giggles I tried the following...
    my $SplitAt = "\\\\"; my @SubDirs = split($SplitAt, $PathOnly);
    BOOM !!! I get the correct result (I actually thought of this whilst typing the comment but figured the info might help someone at some point in time) So, then I tried the following...
    my @SubDirs = split("\\\\", $PathOnly);
    This also worked as I wanted. So, my original question has changed from "How do I split a Windows path on \ ?" to "Why does "\\\\" work in the split statement? I have been working with Perl on and off for about 20 years but I am by no means an expert, but this conundrum completely confuses me. BR, Steve

      split does not take a string as its first argument, it takes a regular expression.

      So the correct usage is

      @SubDirs = split(/\\/, $PathOnly);

      But maybe you want to use File::Spec instead, or Path::Tiny?

      my @SubDirs = File::Spec->splitdir( $PathOnly );

      Either of the literals  "\\\\" or  '\\\\' interpolate in a  m// or  qr// operator to a pair of backslash characters  \\ (escaped-backslash) which match a single backslash character.

      c:\@Work\Perl\monks>perl -wMstrict -MData::Dump -le "print '\\'; print '\\\\'; print qq{\\}; print qq{\\\\}; ;; print qr{\\}; my $bb = '\\\\'; print qr{$bb}; ;; my $PathOnly = 'c:\Dir1\Dir2\Dir3'; ;; my @SubDirs = split(m/\\/, $PathOnly); dd \@SubDirs; ;; @SubDirs = split($bb, $PathOnly); dd \@SubDirs; " \ \\ \ \\ (?-xism:\\) (?-xism:\\) ["c:", "Dir1", "Dir2", "Dir3"] ["c:", "Dir1", "Dir2", "Dir3"]


      Give a man a fish:  <%-{-{-{-<

      Corion already commented on the problem you noted, but there's another: your $PathOnly is in double quotes, so the backslashes there are interpreted as escape sequences.

      Also, should you ever be in need of a string consisting of a single backslash, you could use "\N{REVERSE SOLIDUS}" (prior to v5.16, you have to use charnames explicitly for this to work). That is complicated, but whoever reads that, will have no doubt that you indeed want a single backslash that is not part of an escape secuence.
        ... your $PathOnly is in double quotes, so the backslashes there are i +nterpreted as escape sequences.

        SteveDC:   Ah, yes!

        c:\@Work\Perl\monks>perl -wMstrict -le "my $PathOnly = qq{c:\Dir1\Dir2\taint_so}; print qq{>$PathOnly<}; " Unrecognized escape \D passed through at -e line 1. Unrecognized escape \D passed through at -e line 1. >c:Dir1Dir2 aint_so<
        Don't know how you missed the warnings. You do have warnings enabled, right? Right?


        Give a man a fish:  <%-{-{-{-<

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1016381]
Front-paged by ww
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (9)
As of 2024-04-19 09:33 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found