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

Monks,

I have what I thought was a simple problem, but I've been playing with it for a good while now and can't see my way through it - I'm hoping that someone here will have some suggestions.

I'm trying to convert literal strings, for example '\n', into their respective escape characters, such that the resulting character is interpreted as a newline.

I've tried (among many other things) evaling the literal, using sprintf on the literal and attempting to substitute the literal for an evaluated character (s/(\\\w)/$1/e), but none of these seem to do the trick - I either end up with the string I had before, or an undefined variable.

Does anyone know of a way to convert like this? Any suggestions would be appreciated.

Thanks,
-- Foxcub
A friend is someone who can see straight through you, yet still enjoy the view. (Anon)

Replies are listed 'Best First'.
Re: Substituting literal strings for escape characters
by valdez (Monsignor) on Feb 18, 2003 at 13:31 UTC
Re: Substituting literal strings for escape characters
by jmcnamara (Monsignor) on Feb 18, 2003 at 13:46 UTC

    Here is one way:
    #!/usr/bin/perl -wl use strict; my %esc = ( '\0' => "\0", '\a' => "\a", '\b' => "\b", '\t' => "\t", '\n' => "\n", '\v' => "\013", '\f' => "\f", '\r' => "\r", ); my $literal = 'aaa\tbbb\nccc\tddd\t\\\\end'; (my $escaped = $literal) =~ s/(\\.)/exists $esc{$1} ? $esc{$1} : $ +1/eg; print $literal, "\n"; print $escaped; __END__ Prints: aaa\tbbb\nccc\tddd\t\\end aaa bbb ccc ddd \\end

    Backslash has to be escaped in single quoted strings so it doesn't require a substitution.

    The escape characters shown are the usual shell escapes. Perl adds a few more such as \e. Note the exists() isn't strictly required.

    --
    John.

      This is a lot more complex than it needs to be, and only recognizes a small subset of possible scapes. Plus, there are escapes with an argument (like \012 or \x0A or \cJ for newline), which would make a very big hash or more complex code.

      A regex-with-eval-substitution like the ones below is a better solution.

Re: Substituting literal strings for escape characters
by steves (Curate) on Feb 18, 2003 at 13:46 UTC
    One way using eval is:
    my $x = '\t\t\n\t\n\n'; eval "\$x = \"$x\""; print "$x";
      Just for fun, try that with:
      my $x = '";warn q[this is code!] for 1..20;"';
      :-)

      Hint: Don't use eval() carelessly... your version allows arbitrary code to be placed in the input string, which will get executed

        Good point. I don't typically use eval on data that comes in externally -- only on data my program controls.

Re: Substituting literal strings for escape characters
by Tomte (Priest) on Feb 18, 2003 at 13:32 UTC

    My first shot at the commandline might provide a start for you:

    perl -e "\$test='a\n\nb'; \$test =~ s/(\\\\\\w)/pack(\"h\",\"\$1\")/ge +;print \$test"
    yields
    a b
    Notice that there is a blank in front of the b
    I don't know where the blank originates from, but to use pack seems to be starting point

    hth

    Update:pure luck that it kind of worked with \n, basically b***sh*t.

    Update II:I was asked to beep 'the word' out, so that corporate filters don't choke on this node. I'm in no way expecting anybody in here to be offended by it :-)

    regards,
    tomte


Re: Substituting literal strings for escape characters
by hv (Prior) on Feb 18, 2003 at 13:57 UTC

    You are going in the right direction with the evalled substitution, but consider what you end up with: eval "\n", which is much the same as

    eval { }

    The obvious next thing to try is adding the quotes:

    s/(\\\w)/"$1"/eg;
    but that doesn't do the trick, and brainfade prevents me from explaining why. :(

    Adding a second evaluation step is enough to achieve the desired result:

    s/(\\\w)/qq{"$1"}/eeg;

    Hugo
      That contains a bug:

      The input string:  \\n should be interpreted as:  \n (as in, the backslash escapes the backslash) but in your case, it becomes a backslash followed by a newline.

      The solution is:  s/(\\.)/qq["$1"]/eegs

      Btw, the reason "$1"/e isn't sufficient is because when using /e it doesn't interpolate before evaluating, so the expression really becomes "$1" which is identical to $1 - no change happens at all.

      With qq["$1"]/ee however it'll interpolate $1 into a double-quoted string the first time, and then evaluates that double-quoted string

      •Update: to also support multi-char escapes:

      s/(\\(?:\d{1,3}|x[a-fA-F\d]{1,2}|x\{\w*\}|c.|N\{\w*\}|.))/qq["$1"]/eeg +s
      (not fully tested)
Re: Substituting literal strings for escape characters
by JamesNC (Chaplain) on Feb 18, 2003 at 18:18 UTC
    I wrote this a while ago... hope it helps.. you can use it as a package or just copy the sub..
    package Escape; use Exporter; # simple escape module for html stuff # use Escape; # my $data = escape($string_to_escape); @ISA = qw(Exporter); @EXPORT = qw(escape); sub escape { my $data = shift; $data =~ s/\%/%25/g;$data =~ s/\t/%9/g;$data =~ s/\n/A/g; $data =~ s/ /%20/g;$data =~ s/\!/%21/g;$data =~ s/\"/22/g; $data =~ s/\#/%23/g;$data =~ s/\$/24/g;$data =~ s/\&/%26/g; $data =~ s/\'/%27/g;$data =~ s/\(/%28/g;$data =~ s/\)/%29/g; $data =~ s/\*/%2A/g;$data =~ s/\+/%2B/g;$data =~ s/\,/%2C/g; $data =~ s/\-/%2D/g;$data =~ s/\./%2E/g;$data =~ s/\//%2F/g; $data =~ s/\:/%3A/g;$data =~ s/\;/%3B/g;$data =~ s/\</%3C/g; $data =~ s/\=/%3D/g;$data =~ s/\>/%3E/g;$data =~ s/\?/%3F/g; $data =~ s/\@/%40/g;$data =~ s/\[/%5B/g;$data =~ s/\\/%5C/g; $data =~ s/\]/%5D/g;$data =~ s/\^/%5E/g;$data =~ s/\_/%5F/g; $data =~ s/\`/%60/g;$data =~ s/\{/%7B/g;$data =~ s/\|/%7C/g; $data =~ s/\}/%7D/g;$data =~ s/\~/%7E/g; return $data; } 1;
    Cheers, James
      it was pointed out to me that I was way off the mark.. sorry... perhaps this little snippet will give you something to experiment with that may provide you with the insight you are hoping to find
      use strict; #Experiment with commenting out the next line! binmode STDIN, ":raw"; print "Just hit the enter key!\n"; my $char = <STDIN>; my $bits = unpack "B*", $char; print "[Enter]'ed bits: $bits \n"; my $bits = unpack "B*", '\n'; print "The literal bits: $bits \n";
Re: Substituting literal strings for escape characters
by Anonymous Monk on Feb 18, 2003 at 22:57 UTC
    Code:
    
    $x = '\n'; print "var :==$x==\n"; print "eval :==", eval( qq{ sprintf "%s","$x" } ), "==\n";
    Output:
    var :==\n== eval :== ==
    You need double-quotish interpolation *twice*. :)