Conversión entre cadenas y ArrayBuffers
¿Existe una técnica comúnmente aceptada para convertir de manera eficiente cadenas de JavaScript a ArrayBuffers y viceversa? Específicamente, me gustaría poder escribir el contenido de un ArrayBuffer localStorage
y luego volver a leerlo.
Actualización 2016 : cinco años después, ahora hay nuevos métodos en las especificaciones (consulte el soporte a continuación) para convertir entre cadenas y matrices escritas utilizando la codificación adecuada.
Codificador de texto
El TextEncoder
representa :
La
TextEncoder
interfaz representa un codificador para un método específico, es decir, una codificación de caracteres específica, comoutf-8
,Un codificador toma un flujo de puntos de código como entrada y emite un flujo de bytes.iso-8859-2
,koi8
,cp1261
,gbk
, ...
Nota de cambio desde que se escribió lo anterior: (ibid.)
Nota: Firefox, Chrome y Opera solían admitir tipos de codificación distintos de utf-8 (como utf-16, iso-8859-2, koi8, cp1261 y gbk). A partir de Firefox 48 [...], Chrome 54 [...] y Opera 41, no hay otros tipos de codificación disponibles aparte de utf-8, para cumplir con las especificaciones.*
*) Especificaciones actualizadas (W3) y aquí (whatwg).
Después de crear una instancia, TextEncoder
tomará una cadena y la codificará usando un parámetro de codificación determinado:
if (!("TextEncoder" in window))
alert("Sorry, this browser does not support TextEncoder...");
var enc = new TextEncoder(); // always utf-8
console.log(enc.encode("This is a string converted to a Uint8Array"));
Luego, por supuesto, utiliza el .buffer
parámetro en el resultado Uint8Array
para convertir la capa subyacente ArrayBuffer
a una vista diferente si es necesario.
Solo asegúrese de que los caracteres de la cadena se adhieran al esquema de codificación; por ejemplo, si usa caracteres fuera del rango UTF-8 en el ejemplo, se codificarán en dos bytes en lugar de uno.
Para uso general, usaría la codificación UTF-16 para cosas como localStorage
.
Decodificador de texto
Asimismo, el proceso opuesto utilizaTextDecoder
:
La
TextDecoder
interfaz representa un decodificador para un método específico, es decir, una codificación de caracteres específica, comoutf-8
,iso-8859-2
,koi8
,cp1261
,gbk
... Un decodificador toma un flujo de bytes como entrada y emite un flujo de puntos de código.
Todos los tipos de decodificación disponibles se pueden encontrar aquí .
if (!("TextDecoder" in window))
alert("Sorry, this browser does not support TextDecoder...");
var enc = new TextDecoder("utf-8");
var arr = new Uint8Array([84,104,105,115,32,105,115,32,97,32,85,105,110,116,
56,65,114,114,97,121,32,99,111,110,118,101,114,116,
101,100,32,116,111,32,97,32,115,116,114,105,110,103]);
console.log(enc.decode(arr));
La biblioteca MDN StringView
Una alternativa a estas es utilizar la StringView
biblioteca (con licencia lgpl-3.0) cuyo objetivo es:
- para crear una interfaz tipo C para cadenas (es decir, una matriz de códigos de caracteres, un ArrayBufferView en JavaScript) basada en la interfaz JavaScript ArrayBuffer
- para crear una biblioteca altamente extensible que cualquiera pueda ampliar agregando métodos al objeto StringView.prototype
- para crear una colección de métodos para objetos similares a cadenas (desde ahora: stringViews) que funcionan estrictamente en matrices de números en lugar de crear nuevas cadenas de JavaScript inmutables
- para trabajar con codificaciones Unicode distintas a las DOMStrings UTF-16 predeterminadas de JavaScript
dando mucha más flexibilidad. Sin embargo, sería necesario que vinculáramos o incrustáramos esta biblioteca mientras TextEncoder
/ TextDecoder
se integra en los navegadores modernos.
Apoyo
A julio/2018:
TextEncoder
(Experimental, en pista estándar)
Chrome | Edge | Firefox | IE | Opera | Safari
----------|-----------|-----------|-----------|-----------|-----------
38 | ? | 19° | - | 25 | -
Chrome/A | Edge/mob | Firefox/A | Opera/A |Safari/iOS | Webview/A
----------|-----------|-----------|-----------|-----------|-----------
38 | ? | 19° | ? | - | 38
°) 18: Firefox 18 implemented an earlier and slightly different version
of the specification.
WEB WORKER SUPPORT:
Experimental, On Standard Track
Chrome | Edge | Firefox | IE | Opera | Safari
----------|-----------|-----------|-----------|-----------|-----------
38 | ? | 20 | - | 25 | -
Chrome/A | Edge/mob | Firefox/A | Opera/A |Safari/iOS | Webview/A
----------|-----------|-----------|-----------|-----------|-----------
38 | ? | 20 | ? | - | 38
Data from MDN - `npm i -g mdncomp` by epistemex
Aunque las soluciones de Dennis y gengkev para usar Blob/FileReader funcionan, no sugeriría adoptar ese enfoque. Es un enfoque asíncrono para un problema simple y es mucho más lento que una solución directa. Hice una publicación en html5rocks con una solución más simple y (mucho más rápida): http://updates.html5rocks.com/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
Y la solución es:
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;
}
EDITAR:
La API de codificación ayuda a resolver el problema de conversión de cadenas. Consulte la respuesta de Jeff Posnik en Html5Rocks.com al artículo original anterior.
Extracto:
La API de codificación simplifica la traducción entre bytes sin formato y cadenas de JavaScript nativas, independientemente de con cuál de las muchas codificaciones estándar necesite trabajar.
<pre id="results"></pre>
<script>
if ('TextDecoder' in window) {
// The local files to be fetched, mapped to the encoding that they're using.
var filesToEncoding = {
'utf8.bin': 'utf-8',
'utf16le.bin': 'utf-16le',
'macintosh.bin': 'macintosh'
};
Object.keys(filesToEncoding).forEach(function(file) {
fetchAndDecode(file, filesToEncoding[file]);
});
} else {
document.querySelector('#results').textContent = 'Your browser does not support the Encoding API.'
}
// Use XHR to fetch `file` and interpret its contents as being encoded with `encoding`.
function fetchAndDecode(file, encoding) {
var xhr = new XMLHttpRequest();
xhr.open('GET', file);
// Using 'arraybuffer' as the responseType ensures that the raw data is returned,
// rather than letting XMLHttpRequest decode the data first.
xhr.responseType = 'arraybuffer';
xhr.onload = function() {
if (this.status == 200) {
// The decode() method takes a DataView as a parameter, which is a wrapper on top of the ArrayBuffer.
var dataView = new DataView(this.response);
// The TextDecoder interface is documented at http://encoding.spec.whatwg.org/#interface-textdecoder
var decoder = new TextDecoder(encoding);
var decodedString = decoder.decode(dataView);
// Add the decoded file's text to the <pre> element on the page.
document.querySelector('#results').textContent += decodedString + '\n';
} else {
console.error('Error while requesting', file, this);
}
};
xhr.send();
}
</script>
Puede utilizar TextEncoder
y TextDecoder
desde el estándar de codificación , que se completa mediante la biblioteca stringencoding , para convertir cadenas hacia y desde ArrayBuffers:
var uint8array = new TextEncoder().encode(string);
var string = new TextDecoder(encoding).decode(uint8array);
Blob es mucho más lento queString.fromCharCode(null,array);
pero eso falla si el búfer de la matriz se vuelve demasiado grande. La mejor solución que he encontrado es usarla String.fromCharCode(null,array);
y dividirla en operaciones que no acaben con la pila, pero que sean más rápidas que un solo carácter a la vez.
La mejor solución para un búfer de matriz grande es:
function arrayBufferToString(buffer){
var bufView = new Uint16Array(buffer);
var length = bufView.length;
var result = '';
var addition = Math.pow(2,16)-1;
for(var i = 0;i<length;i+=addition){
if(i + addition > length){
addition = length - i;
}
result += String.fromCharCode.apply(null, bufView.subarray(i,i+addition));
}
return result;
}
Descubrí que esto es aproximadamente 20 veces más rápido que usar blob. También funciona para cadenas grandes de más de 100 MB.