3

What is the cleanest Ruby way to convert a number to an ASCII string?

for example, a = 0x68656c6c6f should become a = "hello".

In normal C without libraries, I would use a 0xFF mask which I kept shifting. Somehow I've the feeling Ruby has shorter/less explicit ways to do this.

I'm using Ruby 1.8.7.

4
  • While I dont like the fact that I need to convert it to a string first ("68656c6c6f"), Array#pack("H*") seems the way to go Commented Jan 23, 2012 at 15:53
  • I dont like the fact that I need to convert it to a string first, then use .to_s(16) Commented Jan 23, 2012 at 16:11
  • Then I still convert it to a string :). What I meant is I hoped for a ruby-ish solution that could convert it to a ascii string without having to convert it to a hex string first. Commented Jan 24, 2012 at 7:46
  • Converting it to a string and then unpacking it IS a Ruby-like way to do it. You could also do it the C/assembler way and shift and mask bytes. Commented Jan 24, 2012 at 7:50

5 Answers 5

6
["%x" % 0x68656c6c6f].pack("H*")

Update: Another crazy idea, which is probably overkill in your case, but this one works right with leading zeros. In fact it's just shift, but can be used with various function like map, inject, each etc.

class S
  include Enumerable

  def initialize(i)
    @i = i
  end

  def each(&block)
    while @i > 0
      @i, b = @i.divmod(256)
      block[b.chr]
    end
  end
end

S.new(0x0168656c6c6f).inject{ |a, c| c + a }
Sign up to request clarification or add additional context in comments.

2 Comments

I just noticed something: this only works when there are no leading zeroes!! ["%x" % 0x0168656c6c6f].pack("H*") wouldnt give an expected result ("\001hello")
I wish I could upvote again, great job! The whole construction is indeed a bit overkill in my case and I think it doesn't increase readability in my case. For now, I'll stick with the old-fashioned shift
5
["68656c6c6f"].pack("H*") #=> "hello"

Have a look at the docs for Array, specifically the pack and unpack methods.

3 Comments

You are almost there, but a was supposed to be a number, not string
You're right, I was just pointing him in the right direction though, the pack and unpack methods that are made for this. Your answer with the %x is clever, upvoting it.
I commented on the answer with %x, it doesn't work as I would expect
3

I think that there's nothing wrong with writing C-like code for the problem that you described. You are dealing with low-level processing, so it's acceptable to use low-level syntax:

n = 0x68656c6c6f
s = ''
while n > 0
  p = n & 0xff
  n = n >> 8
  s = p.chr + s
end
puts s

There must be ways to make the code feel more like Ruby, but, for this problem, I think it's a good alternative. If you had the sequence of characters in an array instead, it would be easier:

puts [0x68, 0x65, 0x6c, 0x6c, 0x6f].map{|n| n.chr}.reduce(:+)

3 Comments

Thanks for your answer. For me, the whole bit shifting is more clear than using map+chr+reduce on one line, since I know Ruby is very powerful and has a compact way to do anything, I was wondering how 'Ruby-guys' look at this problem :-). For now, I'll stick with the shifting.
Meanwhile n, p = n.divmod(256) will save you one line
To convert array of bytes to string you can also do this: [0x68, 0x65, 0x6c, 0x6c, 0x6f].pack 'c*'
1
a = "0x68656c6c6f"
a = a[2..-1] # get rid of the 0x
a.scan(/../).each { |s| puts s.hex.chr }
H
e
l
l
o

Comments

0

Try this

"0x68656c6c6f"[2..-1].gsub(/../) { |val| val.hex.chr } => "hello" 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.