Conversión de matriz de bytes a cadena en javascript

80

¿Cómo convierto una matriz de bytes en una cadena?

He encontrado estas funciones que hacen lo contrario:

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

Pero, ¿cómo hago para que las funciones funcionen al revés?

Gracias.

Shao

user385579
fuente
¿Desea convertir una matriz de bytes en una cadena o una matriz de bits en una cadena?
mcandre
Consulte también la solución adecuada para la matriz utf8: Uint8Array to string en Javascript
Vadzim

Respuestas:

82

Necesita analizar cada octeto hasta el número y usar ese valor para obtener un carácter, algo como esto:

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";

Editar: Sí, su actual string2Binse puede escribir más brevemente:

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

Pero al mirar la documentación que vinculó, creo que el setBytesParametermétodo espera que la matriz de blobs contenga los números decimales, no una cadena de bits , por lo que podría escribir algo como esto:

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ó
fuente
Gracias por la respuesta súper rápida. Un par de preguntas ... 1) Su función bin2String es impresionante: solo 5 líneas de código. ¿Se puede cambiar la función string2bin para hacer uso de más funciones de Javascript para acortar la función y la subfunción? .....
user385579
1
2) La razón por la que necesito estas conversiones es porque estoy capturando una firma y tengo que convertirla para completar un campo BLOB en la base de datos. El problema es que, mientras estas 2 funciones funcionan, algo más va mal. Lo principal es que cuando recupero un BLOB de la base de datos, entra en un objeto de matriz de bytes. Sin embargo, cuando estoy escribiendo el BLOB en la base de datos después de ejecutarlo a través de la función original, no es un objeto de matriz de bytes. Esto podría ser lo que está causando el problema. ¿Algunas ideas?
user385579
dcx.sybase.com/index.html#1101en/ulmbus_en11/… Esta es la sintaxis que utilizo para configurar los datos.
user385579
4
String.fromCharCode.apply(String, array)no es seguro para cadenas muy largas en Safari. Hay un problema en JavaScriptCore que significa que las funciones no pueden tomar más de 65536 argumentos, o se lanzará un RangeError. También bloquea el navegador en matrices un poco más pequeñas que eso. Ver bugs.webkit.org/show_bug.cgi?id=80797
Mateo
4
Falla para caracteres utf-8 de varios bytes, es decir: bin2String([0xE2, 0x98, 0xB9])
Brad Kent
49

Simplemente applysu matriz de bytes a String.fromCharCode. Por ejemplo

String.fromCharCode.apply(null, [102, 111, 111]) es igual a 'foo'.

Advertencia: funciona para matrices de menos de 65535. Documentos de MDN aquí .

Bogdan D
fuente
Esto ya ha sido demostrado por la respuesta aceptada hace 6 años.
Balthazar
2
aah, de hecho, me perdí esa línea. Básicamente, estaba buscando una frase breve y descarté esa respuesta larga y editada (tal vez demasiado apresurada).
Bogdan D
Oh, está bien, eso tiene sentido :)
Balthazar
11
Aunque es una repetición, su brevedad lo hace mejor que la respuesta aceptada.
Rich Apodaca
22

Pruebe la nueva API de codificación de texto:

// 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 which is ascii.
let str = new TextDecoder().decode(bytesView); 

console.log(str);

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

// look, they're the same!
console.log(bytes2);
console.log(bytesView);

James
fuente
1
Desafortunadamente, IE no lo admite.
Soul_man
Si necesita compatibilidad con UTF-8 e IE, puede utilizar el polyfill FastestSmallestTextEncoderDecoder , recomendado por el sitio web de MDN .
Rosberg Linhares
10

Esto debería funcionar:

String.fromCharCode(...array);

O

String.fromCodePoint(...array)
Rouli Freman
fuente
corto y dulce;)
abhisekp
8

Ese string2Bin se puede escribir de manera aún más sucinta y sin bucles, ¡para empezar!

function string2Bin ( str ) {
    return str.split("").map( function( val ) { 
        return val.charCodeAt( 0 ); 
    } );
}
cmcnulty
fuente
1
Sería curioso ver si las llamadas de función agregadas ralentizan esto.
jocull
36
Todavía tiene un bucle, simplemente está oculto dentro de map ().
Johannes Lumpe
4

Creo que esto sería más eficiente:

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
fuente
4

Incluso si llego un poco tarde, pensé que sería interesante para los futuros usuarios compartir algunas implementaciones de una sola línea que hice con ES6.

Una cosa que considero importante dependiendo de su entorno y / y lo que hará con los datos es preservar el valor de byte completo. Por ejemplo, (5).toString(2)le dará 101, pero la conversión binaria completa es en realidad 00000101, y es por eso que es posible que deba crear una leftPadimplementación para completar el byte de cadena con ceros a la izquierda. Pero es posible que no lo necesite en absoluto, como demostraron otras respuestas.

Si ejecuta el siguiente fragmento de código, verá que la primera salida es la conversión de la abccadena en una matriz de bytes y, justo después, la transformación de dicha matriz en su cadena correspondiente.

// 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))

Baltasar
fuente
3

Cadena a matriz de bytes: "FooBar".split('').map(c => c.charCodeAt(0));

Matriz de bytes a cadena: [102, 111, 111, 98, 97, 114].map(c => String.fromCharCode(c)).join('');

ZaksBack
fuente
tenga cuidado, esto no es compatible con IE.
tedebus
1

Demasiado tarde para responder, pero si su entrada está en forma de bytes ASCII, entonces puede probar esta solución:

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));
}
indiaaditya
fuente
1

Si está utilizando node.js , puede hacer esto:

yourByteArray.toString('base64');
Caner
fuente
0

No encontré ninguna solución que funcionara con caracteres UTF-8. String.fromCharCodees bueno hasta que encuentre el carácter de 2 bytes.

Por ejemplo, Hüser vendrá como[0x44,0x61,0x6e,0x69,0x65,0x6c,0x61,0x20,0x48,0xc3,0xbc,0x73,0x65,0x72]

Pero si lo revisa,String.fromCharCode tendrá Hüser, ya que cada byte se convertirá en un carácter por separado.

Solución

Actualmente estoy usando la siguiente solución:

function pad(n) { return (n.length < 2 ? '0' + n : n); }
function decodeUtf8(data) {
  return decodeURIComponent(
    data.map(byte => ('%' + pad(byte.toString(16)))).join('')
  );
}
Alma de hombre
fuente
0

Tenía algunas matrices de bytes descifradas con caracteres de relleno y otras cosas que no necesitaba, así que hice esto (probablemente no sea perfecto, pero funciona para mi uso limitado)

var junk = String.fromCharCode.apply(null, res).split('').map(char => char.charCodeAt(0) <= 127 && char.charCodeAt(0) >= 32 ? char : '').join('');
IgnusFast
fuente
0

Si su matriz está codificada en UTF-8 y no puede usar la API TextDecoder porque no es compatible con IE :

  1. Puede utilizar el polyfill FastestSmallestTextEncoderDecoder recomendado por el sitio web de Mozilla Developer Network ;
  2. Puede utilizar esta función que también se proporciona en el sitio web de MDN :

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
fuente
0

La solución más simple que he encontrado es:

var text = atob(byteArray);
William Hampshire
fuente