Today someone asked me an interesting question: how can I print a string in hexadecimal in ruby?
The solution that immediately jumped into my mind was something like “abc”.map {…}. The first problem here is #map is based on #each, and String#each will iterate on lines instead of characters. So “abc”.map {…} will have only 1 iteration, which give you access to “abc” as a whole inside the iteration.
It’s natural that I then went to String#each_byte for help. “abc”.each_byte {…} will iterate on characters. The problem with each_byte is its return value is the string itself, instead of the result after processing like map. We can’t get a one-line solution with each_byte:
res = []
"abc".each_byte {|i| res << i}
res.map {|i| i.to_s(16)}
3 lines for a simple problem! Ugly, right?
Then I remembered the swissarmy knife of ruby: String#unpack. String#unpack takes a string as directives and applies those directives on its caller. Its caller, a string, is used as a (binary) data stream in this process. For example:
"a".unpack('c') # => [97]
‘c’ is a directive which will extract a character as an integer. You can apply many directives at the same time, like:
"abc".unpack('cH') # => [97, "6"]
which will execute ‘c’ on the first character, and ‘H’ on the second. You can append a number or ‘*’ to a directive so it will be executed many times:
"abc".unpack('c2') # => [97, 98]
"abc".unpack('c*') # => [97, 98, 99]
And String#unpack provides us a directive to extract characters into a hex digit! Here’s the solution using unpack to my friend’s question:
"abc".unpack('H*')
There are many directives you can use in unpack. Whenever you want to transform a string into another common format, like ascii, base64, please give String#unpack a glance, it may resolve your problem instantly.
