Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby's pack and unpack explained

Tags:

ruby

pack

Even after reading the standard documentation, I still can't understand how Ruby's Array#pack and String#unpack exactly work. Here is the example that's causing me the most trouble:

irb(main):001:0> chars = ["61","62","63"]
=> ["61", "62", "63"]
irb(main):002:0> chars.pack("H*")
=> "a"
irb(main):003:0> chars.pack("HHH")
=> "```"

I expected both these operations to return the same output: "abc". Each of them "fails" in a different manner (not really a fail since I probably expect the wrong thing). So two questions:

  1. What is the logic behind those outputs?
  2. How can I achieve the effect I want, i.e. transforming a sequence of hexadecimal numbers to the corresponding string. Even better - given an integer n, how to transform it to a string identical to the text file that when is considered as a number (say, in a hex editor) equals n?
like image 801
Gadi A Avatar asked Dec 05 '12 17:12

Gadi A


2 Answers

We were working on a similar problem this morning. If the array size is unknown, you can use:

ary = ["61", "62", "63"]
ary.pack('H2' * ary.size)
=> "abc"

You can reverse it using:

str = "abc"
str.unpack('H2' * str.size)
=> ["61", "62", "63"]
like image 171
the Tin Man Avatar answered Sep 25 '22 06:09

the Tin Man


The 'H' String directive for Array#pack says that array contents should be interpreted as nibbles of hex strings.

In the first example you've provided:

irb(main):002:0> chars.pack("H*")
=> "a"

you're telling to pack the first element of the array as if it were a sequence of nibbles (half bytes) of a hex string: 0x61 in this case that corresponds to the 'a' ASCII character.

In the second example:

irb(main):003:0> chars.pack("HHH")
=> "```"

you're telling to pack 3 elements of the array as if they were nibbles (the high part in this case): 0x60 corresponds to the '`' ASCII character. The low part or second nibble (0x01) "gets lost" due to missing '2' or '*' modifiers for "aTemplateString".

What you need is:

chars.pack('H*' * chars.size)

in order to pack all the nibbles of all the elements of the array as if they were hex strings.

The case of 'H2' * char.size only works fine if the array elements are representing 1 byte only hex strings.

It means that something like chars = ["6161", "6262", "6363"] is going to be incomplete:

2.1.5 :047 > chars = ["6161", "6262", "6363"]
 => ["6161", "6262", "6363"] 
2.1.5 :048 > chars.pack('H2' * chars.size)
 => "abc" 

while:

2.1.5 :049 > chars.pack('H*' * chars.size)
 => "aabbcc"
like image 24
Redsadic Avatar answered Sep 25 '22 06:09

Redsadic