18

How can I convert "1234567890" to "\x12\x34\x56\x78\x90" in Ruby?

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • Judging by the answers, the output is supposed to be "\x12\x34\x56\x78\x90", rather than an Array of Fixnum or a syntactically invalid Hash. – Martin Dorey Jul 26 '13 at 20:55

6 Answers6

34

Try this:

["1234567890"].pack('H*')
patrick
  • 838
  • 8
  • 11
11

Ruby 1.8 -

hex_string.to_a.pack('H*')

Ruby 1.9 / Ruby 1.8 -

Array(hex_string).pack('H*')
Vikrant Chaudhary
  • 11,089
  • 10
  • 53
  • 68
  • File.open('output.txt', 'w+') {|f| f.write(IO.read('input.txt').to_a.pack('H*')) } – neoneye Jun 24 '10 at 14:47
  • I'm not sure how your answer is supposed to work. Could you give a better example using the hex string in the question? When I tried your code I got: `d:\>irb irb(main):001:0> hex_string = "1234567890" => "1234567890" irb(main):002:0> hex_string.to_a.pack('H*') NoMethodError: undefined method `to_a' for "1234567890":String from (irb):2 from C:/Ruby192/bin/irb:12:in `
    ' irb(main):003:0>`
    – ZombieDev Oct 05 '11 at 17:12
  • @ZombieDev, @steenslag Ruby 1.9 doesn't have `String#to_a` method. I've updated my answer to reflect that. – Vikrant Chaudhary May 04 '12 at 05:47
8

Assuming you have a well-formed hexadecimal string (pairs of hex digits), you can pack to binary, or unpack to hex, simply & efficiently, like this:

string = '0123456789ABCDEF'
binary = [string].pack('H*')     # case-insensitive
 => "\x01#Eg\x89\xAB\xCD\xEF"
hex = binary.unpack('H*').first  # emits lowercase
 => "012345679abcdef"
Trashpanda
  • 14,758
  • 3
  • 29
  • 18
4
class String

  def hex2bin
    s = self
    raise "Not a valid hex string" unless(s =~ /^[\da-fA-F]+$/)
    s = '0' + s if((s.length & 1) != 0)
    s.scan(/../).map{ |b| b.to_i(16) }.pack('C*')
  end

  def bin2hex
    self.unpack('C*').map{ |b| "%02X" % b }.join('')
  end

end
McDowell
  • 107,573
  • 31
  • 204
  • 267
1

If you have a string containing numbers and you want to scan each as a numeric hex byte, I think this is what you want:

"1234567890".scan(/\d\d/).map {|num| Integer("0x#{num}")}
Chuck
  • 234,037
  • 30
  • 302
  • 389
-1
(0..4).map { |x| "0x%X" % (("1234567890".to_i(16) >> 8 * x) & 255) }.reverse
McDowell
  • 107,573
  • 31
  • 204
  • 267
Kevin Peterson
  • 7,189
  • 5
  • 36
  • 43