97

How do I convert a byte array into a string?

I have found these functions that do the reverse:

function string2Bin(s) {
    var b = new Array();
    var last = s.length;
    
    for (var i = 0; i < last; i++) {
        var d = s.charCodeAt(i);
        if (d < 128)
            b[i] = dec2Bin(d);
        else {
            var c = s.charAt(i);
            alert(c + ' is NOT an ASCII character');
            b[i] = -1;
        }
    }
    return b;
}

function dec2Bin(d) {
    var b = '';
    
    for (var i = 0; i < 8; i++) {
        b = (d%2) + b;
        d = Math.floor(d/2);
    }
    
    return b;
}

But how do I get the functions working the other way?

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
user385579
  • 973
  • 1
  • 7
  • 4
  • Do you want to convert a byte array to a string, or an array of bits to a string? – mcandre Jul 07 '10 at 14:53
  • See also proper solution for utf8 array: [Uint8Array to string in Javascript](https://stackoverflow.com/questions/8936984/uint8array-to-string-in-javascript) – Vadzim Mar 01 '19 at 16:03

16 Answers16

96

You need to parse each octet back to number, and use that value to get a character, something like this:

function bin2String(array) {
  var result = "";
  for (var i = 0; i < array.length; i++) {
    result += String.fromCharCode(parseInt(array[i], 2));
  }
  return result;
}

bin2String(["01100110", "01101111", "01101111"]); // "foo"

// Using your string2Bin function to test:
bin2String(string2Bin("hello world")) === "hello world";

Edit: Yes, your current string2Bin can be written more shortly:

function string2Bin(str) {
  var result = [];
  for (var i = 0; i < str.length; i++) {
    result.push(str.charCodeAt(i).toString(2));
  }
  return result;
}

But by looking at the documentation you linked, I think that the setBytesParameter method expects that the blob array contains the decimal numbers, not a bit string, so you could write something like this:

function string2Bin(str) {
  var result = [];
  for (var i = 0; i < str.length; i++) {
    result.push(str.charCodeAt(i));
  }
  return result;
}

function bin2String(array) {
  return String.fromCharCode.apply(String, array);
}

string2Bin('foo'); // [102, 111, 111]
bin2String(string2Bin('foo')) === 'foo'; // true
Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838
  • Thanks for the super speedy response. Couple of questions... 1) Your bin2String function is impressive - only 5 lines of code. Can the string2bin function be changed to make use of more Javascript functions to shorten the function and sub-function? ..... – user385579 Jul 07 '10 at 15:23
  • 1
    2) The reason I need these conversions is because I am capturing a signature and I have to convert it to populate a BLOB field in the database. Problem is, whilst these 2 functions work, something else is going wrong. The main thing is that when I retrieve a BLOB from the database it goes into a bytes array object. However, when I am writing the BLOB to the database after running it through the original function it is not a bytes array object. This might be what's causing the problem. Any ideas? – user385579 Jul 07 '10 at 15:24
  • http://dcx.sybase.com/index.html#1101en/ulmbus_en11/ag-apiref-setbytesparameter.html This is the syntax I use to set the data. – user385579 Jul 07 '10 at 15:25
  • Hi Christian. You're an absolute genius! :) Thanks for that - it works perfectly! Many thanks! – user385579 Jul 08 '10 at 08:09
  • 4
    `String.fromCharCode.apply(String, array)` is unsafe for very long strings in Safari. There's an issue in JavaScriptCore which means that functions can't take more than 65536 arguments, or a RangeError will be thrown. It also locks up the browser on arrays slightly smaller than that. See https://bugs.webkit.org/show_bug.cgi?id=80797 – Matthew Mar 12 '12 at 12:16
  • 5
    Fails for multi-byte utf-8 characters ie: `bin2String([0xE2, 0x98, 0xB9])` – Brad Kent May 28 '17 at 02:04
  • Isn't that `result += ` by one char in a loop killing the performance? – Vadzim Mar 01 '19 at 16:04
  • @BradKent `string2Bin(bin2String([0xE2, 0x98, 0xB9])) == [0xE2, 0x98, 0xB9]`, so it doesnt fail. – hldev Nov 07 '22 at 23:40
73

ES6 update

Now, string 'foo' also equals String.fromCharCode(...[102, 111, 111])

Original answer

Simply apply your byte array to String.fromCharCode. For example

String.fromCharCode.apply(null, [102, 111, 111]) equals 'foo'.

MDN docs here.

Caveat: works for arrays shorter than 65535 - MDN docs here.

Bogdan D
  • 5,321
  • 2
  • 31
  • 32
36

Try the new Text Encoding API:

// create an array view of some valid bytes
let bytesView = new Uint8Array([104, 101, 108, 108, 111]);

console.log(bytesView);

// convert bytes to string
// encoding can be specfied, defaults to utf-8
let str = new TextDecoder().decode(bytesView); 

console.log(str);

// convert string to bytes
// encoding can be specfied, defaults to utf-8
let bytes2 = new TextEncoder().encode(str);

// look, they're the same!
console.log(bytes2);
console.log(bytesView);
SuperStormer
  • 4,997
  • 5
  • 25
  • 35
James
  • 30,496
  • 19
  • 86
  • 113
  • 1
    Unfortunately IE doesn't support it. – Soul_man Oct 11 '18 at 14:02
  • If you need UTF-8 and IE support, you can you use the [FastestSmallestTextEncoderDecoder polyfill](https://github.com/anonyco/FastestSmallestTextEncoderDecoder), recommended by the [MDN website](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder). – Rosberg Linhares Dec 05 '19 at 03:26
  • 1
    TextDecoder fails for non printable ASCII chars. Test `new TextEncoder().encode(new TextDecoder().decode(new Uint8Array([255, 255, 255, 255])))` is not `[255, 255, 255, 255]`. To convert byte array to string you use `String.fromCharCode()`, then the inverse operation is possible. – hldev Nov 07 '22 at 23:04
  • Use TextDecoder("UTF-16") to half the string. In 16 bit javascript memory the upper byte is now used, so it halves the memory usage. But files is best with the default "UTF-8" so it got bigger when I tried to download. – Dan Froberg Jul 21 '23 at 01:03
18

This should work:

String.fromCharCode(...array);

Or

String.fromCodePoint(...array)
Rouli Freman
  • 315
  • 3
  • 10
7

That string2Bin can be written even more succinctly, and without any loops, to boot!

function string2Bin ( str ) {
    return str.split("").map( function( val ) { 
        return val.charCodeAt( 0 ); 
    } );
}
cmcnulty
  • 411
  • 4
  • 12
5

String to byte array: "FooBar".split('').map(c => c.charCodeAt(0));

Byte array to string: [102, 111, 111, 98, 97, 114].map(c => String.fromCharCode(c)).join('');

ZaksBack
  • 189
  • 3
  • 10
4

I think this would be more efficient:

function toBinString (arr) {
    var uarr = new Uint8Array(arr.map(function(x){return parseInt(x,2)}));
    var strings = [], chunksize = 0xffff;
    // There is a maximum stack size. We cannot call String.fromCharCode with as many arguments as we want
    for (var i=0; i*chunksize < uarr.length; i++){
        strings.push(String.fromCharCode.apply(null, uarr.subarray(i*chunksize, (i+1)*chunksize)));
    }
    return strings.join('');
}
lovasoa
  • 6,419
  • 1
  • 35
  • 45
4

Even if I'm a bit late, I thought it would be interesting for future users to share some one-liners implementations I did using ES6.

One thing that I consider important depending on your environment or/and what you will do with with the data is to preserve the full byte value. For example, (5).toString(2) will give you 101, but the complete binary conversion is in reality 00000101, and that's why you might need to create a leftPad implementation to fill the string byte with leading zeros. But you may not need it at all, like other answers demonstrated.

If you run the below code snippet, you'll see the first output being the conversion of the abc string to a byte array and right after that the re-transformation of said array to it's corresponding string.

// For each byte in our array, retrieve the char code value of the binary value
const binArrayToString = array => array.map(byte => String.fromCharCode(parseInt(byte, 2))).join('')

// Basic left pad implementation to ensure string is on 8 bits
const leftPad = str => str.length < 8 ? (Array(8).join('0') + str).slice(-8) : str

// For each char of the string, get the int code and convert it to binary. Ensure 8 bits.
const stringToBinArray = str => str.split('').map(c => leftPad(c.charCodeAt().toString(2)))

const array = stringToBinArray('abc')

console.log(array)
console.log(binArrayToString(array))
Preview
  • 35,317
  • 10
  • 92
  • 112
2

If your array is encoded in UTF-8 and you can't use the TextDecoder API because it is not supported on IE:

  1. You can use the FastestSmallestTextEncoderDecoder polyfill recommended by the Mozilla Developer Network website;
  2. You can use this function also provided at the MDN website:

function utf8ArrayToString(aBytes) {
    var sView = "";
    
    for (var nPart, nLen = aBytes.length, nIdx = 0; nIdx < nLen; nIdx++) {
        nPart = aBytes[nIdx];
        
        sView += String.fromCharCode(
            nPart > 251 && nPart < 254 && nIdx + 5 < nLen ? /* six bytes */
                /* (nPart - 252 << 30) may be not so safe in ECMAScript! So...: */
                (nPart - 252) * 1073741824 + (aBytes[++nIdx] - 128 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
            : nPart > 247 && nPart < 252 && nIdx + 4 < nLen ? /* five bytes */
                (nPart - 248 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
            : nPart > 239 && nPart < 248 && nIdx + 3 < nLen ? /* four bytes */
                (nPart - 240 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
            : nPart > 223 && nPart < 240 && nIdx + 2 < nLen ? /* three bytes */
                (nPart - 224 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
            : nPart > 191 && nPart < 224 && nIdx + 1 < nLen ? /* two bytes */
                (nPart - 192 << 6) + aBytes[++nIdx] - 128
            : /* nPart < 127 ? */ /* one byte */
                nPart
        );
    }
    
    return sView;
}

let str = utf8ArrayToString([50,72,226,130,130,32,43,32,79,226,130,130,32,226,135,140,32,50,72,226,130,130,79]);

// Must show 2H₂ + O₂ ⇌ 2H₂O
console.log(str);
Rosberg Linhares
  • 3,537
  • 1
  • 32
  • 35
2

If you are using node.js you can do this:

yourByteArray.toString('base64');
Caner
  • 57,267
  • 35
  • 174
  • 180
1

Too late to answer but if your input is in form of ASCII bytes, then you could try this solution:

function convertArrToString(rArr){
 //Step 1: Convert each element to character
 let tmpArr = new Array();
 rArr.forEach(function(element,index){
    tmpArr.push(String.fromCharCode(element));
});
//Step 2: Return the string by joining the elements
return(tmpArr.join(""));
}

function convertArrToHexNumber(rArr){
  return(parseInt(convertArrToString(rArr),16));
}
0

UPDATE

@rosberg-linhares posted best solution so far to handle UTF8.


Didn't find any solution that would work with UTF-8 characters. String.fromCharCode is good until you meet 2 byte character.

For example word Hüser can come over the wire in form of arraybuffer as [0x48,0xc3,0xbc,0x73,0x65,0x72] (e.g. through websocket connection)

But if you go through it with String.fromCharCode you will have Hüser as each byte will be converted to a char separately, and letter ü is encoded in two bytes.

Solution

Currently I'm using following solution:

function pad(n) { return (n.length < 2 ? '0' + n : n); }
function decodeUtf8(data) {
  return decodeURIComponent(
    data.map(byte => ('%' + pad(byte.toString(16)))).join('')
  );
}
Soul_man
  • 575
  • 5
  • 15
  • Probably you confused plain 8-bit byte array with "UTF-8" byte array. If you want to deal with UTF-8 byte arrays just use the `TextEncoder` and `TextDecoder`. If you deal with plain byte arrays where values are from 0 to 255 you can use `String.fromCharCode()` and `String.charCodeAt()` fine. – hldev Nov 07 '22 at 23:31
  • @hldev probably I did not express myself clear enough. In the world where you still need to support browsers that do not have support of TextEncoder (e.g. Edge 12-18) so you have to think of some solutions that would work for the client over the wire, where you receive surprisingly 8-bit byte arrays and you just know that they are UTF-8 encoded. Solution I provided is about using different decoding method in the browser. Thank you for pointing out on some flaws in my answer :) – Soul_man Nov 09 '22 at 08:31
0

I had some decrypted byte arrays with padding characters and other stuff I didn't need, so I did this (probably not perfect, but it works for my limited use)

var junk = String.fromCharCode.apply(null, res).split('').map(char => char.charCodeAt(0) <= 127 && char.charCodeAt(0) >= 32 ? char : '').join('');
IgnusFast
  • 79
  • 7
0
> const stringToBin = (str) => [...str].map(item=>item.charCodeAt())
> undefined
> stringToBin('hello')
> (5) [104, 101, 108, 108, 111]
> const binToString = (array) => String.fromCharCode(...array) 
> undefined
> binToString(stringToBin('hello'))
> 'hello'
Donghua Liu
  • 1,776
  • 2
  • 21
  • 30
0

What you are looking for is String.fromCharCode

What you want to do is loop through the array of bytes (represented as integers), create the string equivalent and add it to the result:

function bin2String(array) {
  var result = "";
  for (const char of array) {
    result += String.fromCharCode(char);
  }
  return result;
}

console.log(bin2String([116, 104, 101, 32, 114, 101, 115, 117, 108, 116]));

You can also use the Array.Map function to convert the array of bytes into an array of strings, then join them all.

function string2Bin(array) {
  return array.map(byte => String.fromCharCode(byte)).join("");
}

console.log(string2Bin([116, 104, 101, 32, 114, 101, 115, 117, 108, 116]));
Caleb Liu
  • 627
  • 5
  • 18
-3

The simplest solution I've found is:

var text = atob(byteArray);
Kyle Krzeski
  • 6,183
  • 6
  • 41
  • 52