tags:

views:

645

answers:

5

I’d like to write a Perl one-liner to decode a line of ASCII characters encoded as hexadecimal numbers (for example the line 48 54 54 50 should be decoded as HTTP). I came up with this:

perl -nE 'say map(chr, map { qq/0x$_/ } split)'

It prints an empty line. What am I doing wrong and how would you write it?

+6  A: 

This works:

echo '48 54 54 50' | perl -nE 'say map{chr(hex)} split'

I’m assuming you want to feed the data from STDIN.

datageist
Thank you, I did not know `hex`.
zoul
You're welcome. Thanks for fixing the formatting on the post. :)
datageist
+4  A: 

It's your qq/0x$_/ trick that doesn't work. chr expects a number as argument, but gets the string literal "0x48". Use the hex function to convert 48 to a decimal number, like datageist does in his answer.

This works for me:

echo '48 54 54 50' | perl -nE 'say map(chr, map { hex } split)'

Oh, and thanks for introducing me to the say function, I hadn't seen it before.

Hans W
In one-liners it only works with `-E` (as opposed to `-e`), but I guess you already figured that out. Surely beats `print "$_\n"`.
zoul
Thanks for pointing it out, I had to go back and read that perldoc one more time :D
Hans W
Your `map()`'s look awfully inconsistent. Is anything wrong with `map(chr, map(hex, split))` ?
Chris Lutz
Chris Lutz: I built my snippet from zoul's to make the difference clear. But you're right, `map(chr, map(hex, split))` looks nicer.
Hans W
Hmmm... Looks like that could be written: ... | perl -nE 'say map chr, map hex, split' using even fewer punctuation characters (and that's important :)
Adrian Pronk
+3  A: 

Play perlgolf?

-ple y/0-9A-Fa-f//cd;$_=pack"H*",$_
-ple $_=pack"H*",$_,join"",split
-nE say map chr hex,split
-naE say map chr hex,@F
codeholic
+2  A: 
perl -nle 'print map chr hex, split'

or,

perl -ple 's/([[:xdigit:]]{2})/chr hex $1/ge'

or, only if we are really golfing:

perl -ple 's/[A-F0-9]{2}/chr hex$&/ige'

perl -anle 'print chr hex for@F'
Sinan Ünür
`perl -0x20 -pe'$_=chr hex$_'` is shorter.
Brad Gilbert
+3  A: 

As always with Perl TIMTOWTDI.

I thought I would submit several options, and show what they would look like if they were written normally. If you want to know more about the command line options perldoc perlrun is a useful resource.


These all output the same thing. With the exception that some of them don't print a newline on the end.

echo '48 54 54 50' | perl -0x20 -pe'$_=chr hex$_'
echo '48 54 54 50' | perl -0x20 -ne'print chr hex$_'
echo '48 54 54 50' | perl -0777 -anE'say map chr,map hex,@F'
echo '48 54 54 50' | perl -0777 -anE'say map{chr hex$_}@F'
echo '48 54 54 50' | perl -0apple'$_=chr hex$_' -0x20
echo '48 54 54 50' | perl -apple'$_=join"",map{chr hex}@F'
echo '48 54 54 50' | perl -lanE'say map{chr hex}@F'

The following is what some of the examples would look like if they were written normally. If you want to figure out what the rest of them do, definitely look at perldoc perlrun.


perl -0x20 -pe'$_=chr hex$_'

This is one is fairly straight forward. It is perhaps the best example here, and is also the shortest one. It pretends that spaces are used to separate lines, so that there is only one letter to deal with inside of the loop.

# perl -0x20 -pe'$_=chr hex$_'
$/ = " ";  # -0 ( input separator )
while( <> ){
  $_ = chr hex $_;
} continue {
  print $_;
}

perl -0apple'$_=chr hex$_' -0x20

This one has a few command line options that don't do anything useful.

  • The first -0 option is there so that -l sets the output separator to an empty string. Which is actually the default for the output separator.
  • There are two -p options where one would have sufficed.
  • The -a option sets up the @F array, but we don't actually use it.

Basically I used -a -l and a second -p so that the options would spell apple. Otherwise this one is the same as the last example.

echo '48 54 54 50' | perl -0x20 -pe'$_=chr hex$_'
# perl -0apple'$_=chr hex$_' -0x20
$/ = "";  # -0 ( input separator )
$\ = $/;  # -l ( output separator )
$/ = " "; # -0x20 ( input separator )
while( <> ){
  @F = split " ", $_; # -a ( unused )
  $_ = chr hex $_;
} continue {
  print $_;
}

perl -lanE'say map{chr hex}@F'

I figured I already spelled apple, I might as well spell lanE.

  • -l isn't really useful, because we already are using say.
  • Used -E instead of -e so that we could use say.
# perl -lanE'say map{chr hex}@F'
$\ = $/; # -l ( output separator set to "\n" )
while( <> ){
  @F = split " ", $_; # -a
  say map { chr hex $_ } @F;
}
Brad Gilbert