45

I would like to build a string from a byte value.

I currently use:

str = " "
str[0] = byte

This seems to work fine but I find it ugly and not very scalable to strings longer than 1 character.

Any idea?

6 Answers 6

72

There is a much simpler approach than any of the above: Array#pack:

>> [65,66,67,68,69].pack('c*')
=>  "ABCDE"

I believe pack is implemented in c in matz ruby, so it also will be considerably faster with very large arrays.

Also, pack can correctly handle UTF-8 using the 'U*' template.

Sign up to request clarification or add additional context in comments.

6 Comments

This answer describes the correct way to do it. But remember to set the encoding correctly in Ruby 1.9 as the answer by grosser points out!
You got 'lucky' with the lowercase c*. You really want C*. See: ruby-doc.org/core-1.9.3/Array.html c is for "8-bit signed (signed char)", C is for "8-bit unsigned (unsigned char)"
Pack can NOT correctly handle UTF-8 using the "U*" template. This is incorrect. "U*" packs an array of Unicode codepoints, not UTF8 bytes.
@A.Wilson see my example in the comments for stackoverflow.com/a/4701955/109618
I did see that, I was wondering specifically if it ever made a difference with pack (rather than unpack). This is still important to avoid confusion, I recognize, I'm just wondering if that's the only reason for the caveat
|
21

for 1.9 you need:

[195,164].pack('c*').force_encoding('UTF-8')

1 Comment

I recommend using C* because you want unsigned integers. c* is for signed integers. Note that: "ä".unpack('c*') == [-61, -92]. What you want is: "ä".unpack('C*') == [195, 164]
10

can't remember if there is a single function that does that:

>> a = [65,66,67]
=> [65, 66, 67]
>> a.map {|x| x.chr}.join
=> "ABC"

4 Comments

Nice, did not know about the chr method
@VincentRobert How can you / can you do this example in that style? [195,164].pack('c*').force_encoding('UTF-8')
Got it: [195,164].map { |x| x.chr }.join.force_encoding('UTF-8')
More compact: a.map(&:chr).join
4

If bytes is an array of Fixnum's you could try this:

bytes.map {|num| num.chr}.join

or this:

s = ''
bytes.each {|i| s << i}

Comments

4

The accepted solution does not work well as of today (2024), it was probably fine in 2009. Nowadays UTF-8 is the de facto Internet encoding standard so we will encode bytes to it. What I found works is a combination of previous good suggestions, I resume the proposed solution in a new answer for ease of future readers reference. bye

# BAD, previously suggested, today bytes do not encode only ASCII 
irb> "a α b β".bytes.pack("c*")  
 => "a \xCE\xB1 b \xCE\xB2" 

# BAD, previously suggested, pack("U*") works well only if 
# the array of Int represent UTF-8 integers, larger than bytes. 
# try  "a α b β".unpack("U*").pack("U*").
irb> "a α b β".bytes.pack("U*")  
 => "a α b β"

# OK, proposed solution in 2024
irb> "a α b β".bytes.pack("C*").force_encoding("UTF-8") 
 => "a α b β"

Comments

0

This isn't the OP's question, but if you have just a single byte (not in an array) and want to make a string out of it, use chr

c = 65
=> 65
c.chr
=> "A"
c.chr.class
=> String

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.