Wie konvertiert man ArrayBuffer in und aus String?

Renato Mangini

ArrayBuffers werden zum Transport von Rohdaten verwendet und verschiedene neue APIs stützen sich darauf, darunter WebSockets, Web Intents 2](https://www.html5rocks.com/en/tutorials/file/xhr2/) und WebWorkers. Da sie jedoch erst vor Kurzem in der JavaScript-Welt gelandet sind, werden sie manchmal falsch interpretiert oder missbraucht.

Semantisch ist ein ArrayBuffer einfach ein Array von Byte, die über eine bestimmte Maske angesehen werden. Diese Maske, eine Instanz von ArrayBufferView, definiert, wie Bytes so ausgerichtet werden, dass sie der erwarteten Struktur des Inhalts entsprechen. Wenn Sie beispielsweise wissen, dass die Byte in einem ArrayBuffer ein Array aus vorzeichenlosen 16-Bit-Ganzzahlen darstellen, verpacken Sie einfach den ArrayBuffer in einer Uint16Array-Ansicht. Sie können seine Elemente dann mithilfe der Klammern so bearbeiten, als wäre Uint16Array ein Ganzzahlarray:

// suppose buf contains the bytes [0x02, 0x01, 0x03, 0x07]
// notice the multibyte values respect the hardware endianess, which is little-endian in x86
var bufView = new Uint16Array(buf);
if (bufView[0]===258) {   // 258 === 0x0102
    console.log("ok");
}
bufView[0] = 255;    // buf now contains the bytes [0xFF, 0x00, 0x03, 0x07]
bufView[0] = 0xff05; // buf now contains the bytes [0x05, 0xFF, 0x03, 0x07]
bufView[1] = 0x0210; // buf now contains the bytes [0x05, 0xFF, 0x10, 0x02]

Eine häufig gestellte praktische Frage zu ArrayBuffer ist, wie ein String in ein ArrayBuffer konvertiert wird und umgekehrt. Da ein ArrayBuffer in Wirklichkeit ein Bytearray ist, müssen sich beide Seiten darüber einigen, wie die Zeichen im String als Byte dargestellt werden sollen. Sie haben diese "Vereinbarung" wahrscheinlich schon einmal gesehen: Es handelt sich um die Zeichencodierung des Strings (und die üblichen "Vereinbarungsbedingungen" sind beispielsweise Unicode UTF-16 und iso8859-1). Sofern Sie und die andere Partei also eine UTF-16-Codierung vereinbart haben, könnte der Conversion-Code in etwa so aussehen:

function ab2str(buf) {
    return String.fromCharCode.apply(null, new Uint16Array(buf));
}
function str2ab(str) {
    var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char
    var bufView = new Uint16Array(buf);
    for (var i=0, strLen=str.length; i < strLen; i++) {
    bufView[i] = str.charCodeAt(i);
    }
    return buf;
}

Beachten Sie die Verwendung von Uint16Array. Dies ist eine ArrayBuffer-Ansicht, in der Byte der ArrayBuffers als 16-Bit-Elemente ausgerichtet werden. Die Zeichencodierung selbst, die von String.fromCharCode und str.charCodeAt als Unicode behandelt wird, verarbeitet sie nicht.

Auf eine häufig gestellte StackOverflow-Frage gibt es eine viel bewertete Antwort mit einer etwas komplizierten Lösung für die Konvertierung: Erstellen Sie einen FileReader, der als Converter fungiert, und fügen Sie einen Blob ein, der den String enthält. Obwohl diese Methode funktioniert, ist sie schlecht lesbar und ich vermute, dass sie langsam ist. Da unbegründete Zweifel in der Geschichte der Menschheit zu vielen Fehlern geführt haben, sollten wir hier einen wissenschaftlicheren Ansatz verfolgen. Ich habe beide Methoden mit jsperf ausgeführt. Das Ergebnis bestätigt meinen Verdacht. Sie können sich die Demo hier ansehen.

In Chrome 20 ist die Verwendung des direkten ArrayBuffer-Manipulationscodes in diesem Artikel fast 27-mal schneller als die Verwendung der Methode FileReader/Blob.