template strip_tags remove negrita letra from change javascript serialization arraybuffer typed-arrays

javascript - strip_tags - Conversión entre cadenas y ArrayBuffers



strip_tags javascript (20)

¿Existe una técnica comúnmente aceptada para convertir eficientemente cadenas de JavaScript a ArrayBuffers y viceversa? Específicamente, me gustaría poder escribir el contenido de un ArrayBuffer en localStorage y leerlo de nuevo.


Recomiendo NO usar API en desuso como BlobBuilder

BlobBuilder ha sido desaprobado por mucho tiempo por el objeto Blob. Compare el código en la respuesta de Dennis, donde se usa BlobBuilder, con el siguiente código:

function arrayBufferGen(str, cb) { var b = new Blob([str]); var f = new FileReader(); f.onload = function(e) { cb(e.target.result); } f.readAsArrayBuffer(b); }

Observe cuánto más limpio y menos hinchado se compara con el método desaprobado ... Sí, definitivamente esto es algo que debe considerar aquí.


Todo lo siguiente es acerca de cómo obtener cadenas binarias de búferes de matriz

Recomiendo no usar

var binaryString = String.fromCharCode.apply(null, new Uint8Array(arrayBuffer));

porque

  1. se bloquea en búferes grandes (alguien escribió sobre el tamaño "mágico" de 246300, pero obtuve el error Maximum call stack size exceeded en el búfer de 120000 bytes (Chrome 29))
  2. tiene muy bajo rendimiento (ver más abajo)

Si necesita exactamente una solución síncrona, use algo como

var binaryString = '''', bytes = new Uint8Array(arrayBuffer), length = bytes.length; for (var i = 0; i < length; i++) { binaryString += String.fromCharCode(bytes[i]); }

Es tan lento como el anterior pero funciona correctamente. Parece que en el momento de escribir esto no hay una solución síncrona bastante rápida para ese problema (todas las bibliotecas mencionadas en este tema utilizan el mismo enfoque para sus características síncronas).

Pero lo que realmente recomiendo es usar el enfoque Blob + FileReader

function readBinaryStringFromArrayBuffer (arrayBuffer, onSuccess, onFail) { var reader = new FileReader(); reader.onload = function (event) { onSuccess(event.target.result); }; reader.onerror = function (event) { onFail(event.target.error); }; reader.readAsBinaryString(new Blob([ arrayBuffer ], { type: ''application/octet-stream'' })); }

La única desventaja (no para todos) es que es asíncrono . ¡Y es aproximadamente 8-10 veces más rápido que las soluciones anteriores! (Algunos detalles: la solución síncrona en mi entorno tomó 950-1050 ms para el búfer de 2.4Mb, pero la solución con FileReader tuvo un tiempo de alrededor de 100-120 ms para la misma cantidad de datos. Y he probado ambas soluciones síncronas en el búfer de 100Kb. casi al mismo tiempo, por lo que el bucle no es mucho más lento al usar ''aplicar''.)

Por cierto aquí: http://updates.html5rocks.com/2012/06/How-to-convert-ArrayBuffer-to-and-from-String autor de http://updates.html5rocks.com/2012/06/How-to-convert-ArrayBuffer-to-and-from-String compara dos enfoques como yo y obtengo resultados completamente opuestos ( su código de prueba está aquí ) ¿Por qué resultados tan diferentes? Probablemente debido a su cadena de prueba que tiene 1Kb de largo (lo llamó "veryLongStr"). Mi memoria intermedia era una imagen JPEG realmente grande de tamaño de 2.4Mb.


( Actualización Consulte la segunda mitad de esta respuesta, donde (con suerte) he proporcionado una solución más completa).

También me encontré con este problema, lo siguiente me funciona en FF 6 (por una dirección):

var buf = new ArrayBuffer( 10 ); var view = new Uint8Array( buf ); view[ 3 ] = 4; alert(Array.prototype.slice.call(view).join(""));

Desafortunadamente, por supuesto, usted termina con representaciones de texto ASCII de los valores en la matriz, en lugar de caracteres. Aún así (debería ser) mucho más eficiente que un bucle, sin embargo. p.ej. Para el ejemplo anterior, el resultado es 0004000000 , en lugar de varios caracteres nulos y un chr (4).

Editar:

Después de buscar en MDC here , puede crear un ArrayBuffer desde un Array siguiente manera:

var arr = new Array(23); // New Uint8Array() converts the Array elements // to Uint8s & creates a new ArrayBuffer // to store them in & a corresponding view. // To get at the generated ArrayBuffer, // you can then access it as below, with the .buffer property var buf = new Uint8Array( arr ).buffer;

Para responder a su pregunta original, esto le permite convertir ArrayBuffer <-> String siguiente manera:

var buf, view, str; buf = new ArrayBuffer( 256 ); view = new Uint8Array( buf ); view[ 0 ] = 7; // Some dummy values view[ 2 ] = 4; // ... // 1. Buffer -> String (as byte array "list") str = bufferToString(buf); alert(str); // Alerts "7,0,4,..." // 1. String (as byte array) -> Buffer buf = stringToBuffer(str); alert(new Uint8Array( buf )[ 2 ]); // Alerts "4" // Converts any ArrayBuffer to a string // (a comma-separated list of ASCII ordinals, // NOT a string of characters from the ordinals // in the buffer elements) function bufferToString( buf ) { var view = new Uint8Array( buf ); return Array.prototype.join.call(view, ","); } // Converts a comma-separated ASCII ordinal string list // back to an ArrayBuffer (see note for bufferToString()) function stringToBuffer( str ) { var arr = str.split(",") , view = new Uint8Array( arr ); return view.buffer; }

Para su comodidad, aquí hay una function para convertir una String Unicode sin procesar en un ArrayBuffer (solo funcionará con caracteres ASCII / de un byte)

function rawStringToBuffer( str ) { var idx, len = str.length, arr = new Array( len ); for ( idx = 0 ; idx < len ; ++idx ) { arr[ idx ] = str.charCodeAt(idx) & 0xFF; } // You may create an ArrayBuffer from a standard array (of values) as follows: return new Uint8Array( arr ).buffer; } // Alerts "97" alert(new Uint8Array( rawStringToBuffer("abc") )[ 0 ]);

Lo anterior le permite pasar de ArrayBuffer -> String & back a ArrayBuffer nuevamente, donde la cadena puede almacenarse en, por ejemplo. .localStorage :)

Espero que esto ayude,

Dan


A diferencia de las soluciones aquí, necesitaba convertir a / desde datos UTF-8. Para este propósito, codifiqué las siguientes dos funciones, usando el truco de (des) escape / (en) decodeURIComponent. Son bastante desperdicio de memoria, asignando 9 veces la longitud de la cadena utf8 codificada, aunque gc debería recuperarlos. Simplemente no los uses para texto de 100mb.

function utf8AbFromStr(str) { var strUtf8 = unescape(encodeURIComponent(str)); var ab = new Uint8Array(strUtf8.length); for (var i = 0; i < strUtf8.length; i++) { ab[i] = strUtf8.charCodeAt(i); } return ab; } function strFromUtf8Ab(ab) { return decodeURIComponent(escape(String.fromCharCode.apply(null, ab))); }

Comprobando que funciona:

strFromUtf8Ab(utf8AbFromStr(''latinкирилицаαβγδεζηあいうえお'')) -> "latinкирилицаαβγδεζηあいうえお"


Aunque las soluciones de Dennis y Gengkev de usar Blob / FileReader funcionan, no sugeriría tomar ese enfoque. Es un enfoque asíncrono a un problema simple y es mucho más lento que una solución directa. He hecho 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 cadena . Consulte la respuesta de Jeff Posnik en Html5Rocks.com al artículo original anterior.

Extracto:

La API de codificación facilita la traducción entre bytes en bruto y cadenas de JavaScript nativas, independientemente de con cuál de las muchas codificaciones estándar necesita 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>


Basándome en la respuesta de gengkev, creé funciones para ambas formas, porque BlobBuilder puede manejar String y ArrayBuffer:

function string2ArrayBuffer(string, callback) { var bb = new BlobBuilder(); bb.append(string); var f = new FileReader(); f.onload = function(e) { callback(e.target.result); } f.readAsArrayBuffer(bb.getBlob()); }

y

function arrayBuffer2String(buf, callback) { var bb = new BlobBuilder(); bb.append(buf); var f = new FileReader(); f.onload = function(e) { callback(e.target.result) } f.readAsText(bb.getBlob()); }

Una prueba simple:

string2ArrayBuffer("abc", function (buf) { var uInt8 = new Uint8Array(buf); console.log(uInt8); // Returns `Uint8Array { 0=97, 1=98, 2=99}` arrayBuffer2String(buf, function (string) { console.log(string); // returns "abc" } ) } )


Blob es mucho más lento que String.fromCharCode(null,array);

pero eso falla si el buffer de matriz se vuelve demasiado grande. La mejor solución que he encontrado es usar String.fromCharCode(null,array); y divídalo en operaciones que no hagan explotar la pila, pero son más rápidas que una sola char a la vez.

La mejor solución para el buffer 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; }

Encontré que esto es aproximadamente 20 veces más rápido que usar blob. También funciona para cuerdas grandes de más de 100 mb.


Bueno, aquí hay una manera un tanto complicada de hacer lo mismo:

var string = "Blah blah blah", output; var bb = new (window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder)(); bb.append(string); var f = new FileReader(); f.onload = function(e) { // do whatever output = e.target.result; } f.readAsArrayBuffer(bb.getBlob());

Edición: BlobBuilder ha sido desaprobado durante mucho tiempo en favor del constructor Blob, que no existía cuando escribí por primera vez esta publicación. Aquí hay una versión actualizada. (Y sí, esta siempre ha sido una forma muy tonta de hacer la conversión, ¡pero fue solo por diversión!)

var string = "Blah blah blah", output; var f = new FileReader(); f.onload = function(e) { // do whatever output = e.target.result; }; f.readAsArrayBuffer(new Blob([string]));



Descubrí que tenía problemas con este enfoque, básicamente porque estaba intentando escribir la salida en un archivo y no estaba codificada correctamente. Ya que JS parece usar la codificación UCS-2 ( source , source ), necesitamos extender esta solución un paso más, aquí está mi solución mejorada que me funciona.

No tuve dificultades con el texto genérico, pero cuando se trataba de árabe o coreano, el archivo de salida no tenía todos los caracteres, sino que mostraba caracteres de error.

Salida de archivo: ","10k unit":"",Follow:"Õ©íüY‹","Follow %{screen_name}":"%{screen_name}U“''Õ©íü",Tweet:"ĤüÈ","Tweet %{hashtag}":"%{hashtag} ''ĤüÈY‹","Tweet to %{name}":"%{name}U“xĤüÈY‹"},ko:{"%{followers_count} followers":"%{followers_count}…X /Ì","100K+":"100Ì tÁ","10k unit":"Ì è",Follow:"/°","Follow %{screen_name}":"%{screen_name} Ø /°X0",K:"œ",M:"1Ì",Tweet:"¸","Tweet %{hashtag}":"%{hashtag}

Original: ","10k unit":"万",Follow:"フォローする","Follow %{screen_name}":"%{screen_name}さんをフォロー",Tweet:"ツイート","Tweet %{hashtag}":"%{hashtag} をツイートする","Tweet to %{name}":"%{name}さんへツイートする"},ko:{"%{followers_count} followers":"%{followers_count}명의 팔로워","100K+":"100만 이상","10k unit":"만 단위",Follow:"팔로우","Follow %{screen_name}":"%{screen_name} 님 팔로우하기",K:"천",M:"백만",Tweet:"트윗","Tweet %{hashtag}":"%{hashtag}

Tomé la información de http://updates.html5rocks.com/2012/06/How-to-convert-ArrayBuffer-to-and-from-String de http://updates.html5rocks.com/2012/06/How-to-convert-ArrayBuffer-to-and-from-String y encontré este post .

Aquí está mi código:

function encode_utf8(s) { return unescape(encodeURIComponent(s)); } function decode_utf8(s) { return decodeURIComponent(escape(s)); } function ab2str(buf) { var s = String.fromCharCode.apply(null, new Uint8Array(buf)); return decode_utf8(decode_utf8(s)) } function str2ab(str) { var s = encode_utf8(str) var buf = new ArrayBuffer(s.length); var bufView = new Uint8Array(buf); for (var i=0, strLen=s.length; i<strLen; i++) { bufView[i] = s.charCodeAt(i); } return bufView; }

Esto me permite guardar el contenido en un archivo sin problemas de codificación.

Cómo funciona: básicamente toma los fragmentos de 8 bytes individuales que componen un carácter UTF-8 y los guarda como caracteres individuales (por lo tanto, un carácter UTF-8 creado de esta manera, podría estar compuesto por 1-4 de estos caracteres). UTF-8 codifica caracteres en un formato que varía de 1 a 4 bytes de longitud. Lo que hacemos aquí es codificar la picadura en un componente URI y luego tomar este componente y traducirlo en el correspondiente carácter de 8 bytes. De esta manera, no perdemos la información proporcionada por los caracteres UTF8 que tienen más de 1 byte de longitud.


Después de jugar con la solución de ArrayBuffer para convertir de ArrayBuffer a String - ab2str (que es el más elegante y útil que he encontrado - ¡gracias!), Tuve algunos problemas al manejar grandes arreglos. Más specefivally, llamando a String.fromCharCode.apply(null, new Uint16Array(buf)); arroja un error:

arguments array passed to Function.prototype.apply is too large .

Para resolverlo (bypass), he decidido manejar la entrada ArrayBuffer en trozos. Así que la solución modificada es:

function ab2str(buf) { var str = ""; var ab = new Uint16Array(buf); var abLen = ab.length; var CHUNK_SIZE = Math.pow(2, 16); var offset, len, subab; for (offset = 0; offset < abLen; offset += CHUNK_SIZE) { len = Math.min(CHUNK_SIZE, abLen-offset); subab = ab.subarray(offset, offset+len); str += String.fromCharCode.apply(null, subab); } return str; }

El tamaño del fragmento se establece en 2^16 porque fue el tamaño que encontré para trabajar en mi entorno de desarrollo. Establecer un valor más alto causó que el mismo error vuelva a ocurrir. Se puede modificar estableciendo la variable CHUNK_SIZE en un valor diferente. Es importante tener un número par.

Nota sobre el rendimiento: no hice ninguna prueba de rendimiento para esta solución. Sin embargo, dado que se basa en la solución anterior y puede manejar matrices grandes, no veo ninguna razón para no usarla.

Cualquier comentario es bienvenido (-:


ES2015:

a=Uint8Array.from(s,(x)=>x.charCodeAt(0))

Uint8Array (33) [2, 134, 140, 186, 82, 70, 108, 182, 233, 40, 143, 247, 29, 76, 245, 206, 29, 87, 48, 160, 78, 225, 242 , 56, 236, 201, 80, 80, 152, 118, 92, 144, 48

s=String.fromCharCode.apply(null,a)

"ºRFl¶é (÷ LõÎW0 Náò8ìÉPPv / 0"


La cadena binaria "nativa" que devuelve atob () es una matriz de 1 byte por carácter.

Así que no deberíamos almacenar 2 bytes en un personaje.

var arrayBufferToString = function(buffer) { return String.fromCharCode.apply(null, new Uint8Array(buffer)); } var stringToArrayBuffer = function(str) { return (new Uint8Array([].map.call(str,function(x){return x.charCodeAt(0)}))).buffer; }


Puede usar TextEncoder y TextDecoder del estándar de codificación , que está rellenado por la biblioteca de codificación de cadenas, para convertir la cadena hacia y desde ArrayBuffers:

var uint8array = new TextEncoder().encode(string); var string = new TextDecoder(encoding).decode(uint8array);


Sí:

const encstr = (`TextEncoder` in window) ? new TextEncoder().encode(str) : Uint8Array.from(str, c => c.codePointAt(0));


Si usó un ejemplo de matriz enorme arr.length=1000000 , puede arr.length=1000000 este código para evitar problemas de devolución de llamada de la pila

function ab2str(buf) { var bufView = new Uint16Array(buf); var unis ="" for (var i = 0; i < bufView.length; i++) { unis=unis+String.fromCharCode(bufView[i]); } return unis }

función inversa mangini respuesta desde arriba

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


Usé esto y funciona para mí.

function arrayBufferToBase64( buffer ) { var binary = ''''; var bytes = new Uint8Array( buffer ); var len = bytes.byteLength; for (var i = 0; i < len; i++) { binary += String.fromCharCode( bytes[ i ] ); } return window.btoa( binary ); } function base64ToArrayBuffer(base64) { var binary_string = window.atob(base64); var len = binary_string.length; var bytes = new Uint8Array( len ); for (var i = 0; i < len; i++) { bytes[i] = binary_string.charCodeAt(i); } return bytes.buffer; }


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 con la codificación adecuada.

TextEncoder

El TextEncoder representa :

La interfaz TextEncoder representa un codificador para un método específico, es decir, una codificación de caracteres específica, como utf-8 , iso-8859-2 , koi8 , cp1261 , gbk , ... Un codificador toma un flujo de puntos de código como entrada y emite un flujo de bytes.

Cambiar nota desde que se escribió lo anterior: (ibid.)

Nota: Firefox, Chrome y Opera solían tener soporte para 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, excepto utf-8, para que coincidan con la especificación. *

*) Especificaciones actualizadas (W3) y here (whatwg).

Después de crear una instancia de TextEncoder , tomará una cadena y la codificará utilizando un parámetro de codificación dado:

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, usa el parámetro .buffer en el Uint8Array resultante para convertir el ArrayBuffer subyacente en 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 utiliza caracteres fuera del rango UTF-8 en el ejemplo, se codificarán a dos bytes en lugar de uno.

Para uso general, usaría la codificación UTF-16 para cosas como localStorage .

TextDecoder

Del mismo modo, el proceso opuesto utiliza el TextDecoder :

La interfaz TextDecoder representa un decodificador para un método específico, que es una codificación de caracteres específica, como utf-8 , iso-8859-2 , koi8 , cp1261 , gbk , ... Un decodificador toma una corriente de bytes como entrada y emite una flujo de puntos de código.

Todos los tipos de decodificación disponibles se pueden encontrar here .

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 estos es usar la biblioteca StringView (con licencia como lgpl-3.0) cuyo objetivo es:

  • para crear una interfaz similar a C para cadenas (es decir, una matriz de códigos de caracteres - un ArrayBufferView en JavaScript) basado en la interfaz ArrayBuffer de JavaScript
  • para crear una biblioteca altamente extensible que cualquiera pueda extender agregando métodos al objeto StringView.prototype
  • para crear una colección de métodos para tales objetos parecidos a cadenas (desde ahora: stringViews) que funcionan estrictamente en matrices de números en lugar de en la creación de nuevas cadenas de JavaScript inmutables
  • para trabajar con codificaciones Unicode que no sean las predeterminadas UTF-16 DOMStrings de JavaScript

dando mucha más flexibilidad. Sin embargo, nos requeriría vincular o incrustar esta biblioteca mientras TextEncoder / TextDecoder se está incorporando en los navegadores modernos.

Apoyo

A partir de 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


stringToArrayBuffer(byteString) { var byteArray = new Uint8Array(byteString.length); for (var i = 0; i < byteString.length; i++) { byteArray[i] = byteString.codePointAt(i); } return byteArray; } arrayBufferToString(buffer) { var byteArray = new Uint8Array(buffer); var byteString = ''''; for (var i = 0; i < byteArray.byteLength; i++) { byteString += String.fromCodePoint(byteArray[i]); } return byteString; }