views:

105

answers:

2

In perl suppose I have a string like 'hello\tworld\n', and what I want is:

'hello  world
'

That is, "hello", then a literal tab character, then "world", then a literal newline. Or equivalently, "hello\tworld\n" (note the double quotes).

In other words, is there a function for taking a string with escape sequences and returning an equivalent string with all the escape sequences interpolated? I don't want to interpolate variables or anything else, just escape sequences like \x, where x is a letter.

+1  A: 

You can do it with 'eval':

my $string = 'hello\tworld\n';
my $decoded_string = eval "\"$string\"";

Note that there are security issues tied to that approach if you don't have 100% control of the input string.

Edit: If you want to ONLY interpolate \x substitutions (and not the general case of 'anything Perl would interpolate in a quoted string') you could do this:

my $string = 'hello\tworld\n';
$string =~ s#([^\\A-Za-z_0-9])#\\$1#gs;
my $decoded_string = eval "\"$string\"";

That does almost the same thing as quotemeta - but exempts '\' characters from being escaped.

Edit2: This still isn't 100% safe because if the last character is a '\' - it will 'leak' past the end of the string though...

Personally, if I wanted to be 100% safe I would make a hash with the subs I specifically wanted and use a regex substitution instead of an eval:

my %sub_strings = (
    '\n' => "\n",
    '\t' => "\t",
    '\r' => "\r",
);

$string =~ s/(\\n|\\t|\\n)/$sub_strings{$1}/gs;
Benjamin Franz
Can I use a combination of `eval` and `quotemeta` to do this safely? Basically, `(my $interpolated_string = eval(q(") . quotemeta($string) . q("))) =~ s/\\(.)/\1/g`.
Ryan Thompson
Not without it killing the interpolation as well. ;)
Benjamin Franz
Well, I only want to interpolate `\x`, where `x` is any letter. Not variables or other stuff.
Ryan Thompson
+5  A: 

Sounds like a problem that someone else would have solved already. I've never used the module, but it looks useful:

use String::Escape qw(unbackslash);
my $s = unbackslash('hello\tworld\n');
FM
Thanks very much for this. I found String::Interpolate, but that does something completely different.
Ryan Thompson
Also, for people using this in Ubuntu 9.10, the version of String::Escape doesn't have `unbackslash`. Instead it has `unprintable`.
Ryan Thompson