Ruby: create a String from bytes

41,076

Solution 1

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.

Solution 2

for 1.9 you need:

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

Solution 3

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"

Solution 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}
Share:
41,076

Related videos on Youtube

Vincent Robert
Author by

Vincent Robert

Excuse any English mistakes :-) I am on Twitter

Updated on November 19, 2020

Comments

  • Vincent Robert
    Vincent Robert over 3 years

    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?

  • Vincent Robert
    Vincent Robert about 15 years
    Nice, did not know about the chr method
  • severin
    severin almost 12 years
    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!
  • David J.
    David J. almost 12 years
    @VincentRobert How can you / can you do this example in that style? [195,164].pack('c*').force_encoding('UTF-8')
  • David J.
    David J. almost 12 years
    Got it: [195,164].map { |x| x.chr }.join.force_encoding('UTF-8')
  • David J.
    David J. almost 12 years
    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)"
  • David J.
    David J. almost 12 years
    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]
  • stephenjudkins
    stephenjudkins over 11 years
    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
    A. Wilson almost 11 years
    @DavidJames can you demonstrate a case where C* works and c* doesn't? All unicode has a 1 in GSB and is therefore "signed", right? But this works fine: irb(main):001:0> puts ["11010111", "10101010"].map{|x|x.to_i(2)}.pack('c*') # (gives the string "ת")
  • David J.
    David J. almost 11 years
    @A.Wilson see my example in the comments for stackoverflow.com/a/4701955/109618
  • A. Wilson
    A. Wilson almost 11 years
    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
  • sscirrus
    sscirrus almost 8 years
    More compact: a.map(&:chr).join