[javascript] Converting byte array to string in javascript

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?

Thanks.

Shao

This question is related to javascript casting

The answer is


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:

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


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

yourByteArray.toString('base64');

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 ); 
    } );
}

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('');

Try the new Text Encoding API:

_x000D_
_x000D_
// create an array view of some valid bytes_x000D_
let bytesView = new Uint8Array([104, 101, 108, 108, 111]);_x000D_
_x000D_
console.log(bytesView);_x000D_
_x000D_
// convert bytes to string_x000D_
// encoding can be specfied, defaults to utf-8 which is ascii._x000D_
let str = new TextDecoder().decode(bytesView); _x000D_
_x000D_
console.log(str);_x000D_
_x000D_
// convert string to bytes_x000D_
// encoding can be specfied, defaults to utf-8 which is ascii._x000D_
let bytes2 = new TextEncoder().encode(str);_x000D_
_x000D_
// look, they're the same!_x000D_
console.log(bytes2);_x000D_
console.log(bytesView);
_x000D_
_x000D_
_x000D_


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('');


This should work:

String.fromCharCode(...array);

Or

String.fromCodePoint(...array)

The simplest solution I've found is:

var text = atob(byteArray);

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

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

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


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

For example Hüser will come as [0x44,0x61,0x6e,0x69,0x65,0x6c,0x61,0x20,0x48,0xc3,0xbc,0x73,0x65,0x72]

But if you go through it with String.fromCharCode you will have Hüser as each byte will be converted to a char separately.

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('')
  );
}

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));
}

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('');
}

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.

_x000D_
_x000D_
// For each byte in our array, retrieve the char code value of the binary value_x000D_
const binArrayToString = array => array.map(byte => String.fromCharCode(parseInt(byte, 2))).join('')_x000D_
_x000D_
// Basic left pad implementation to ensure string is on 8 bits_x000D_
const leftPad = str => str.length < 8 ? (Array(8).join('0') + str).slice(-8) : str_x000D_
_x000D_
// For each char of the string, get the int code and convert it to binary. Ensure 8 bits._x000D_
const stringToBinArray = str => str.split('').map(c => leftPad(c.charCodeAt().toString(2)))_x000D_
_x000D_
const array = stringToBinArray('abc')_x000D_
_x000D_
console.log(array)_x000D_
console.log(binArrayToString(array))
_x000D_
_x000D_
_x000D_