Gibt es eine allgemein akzeptierte Technik zum effizienten Konvertieren von JavaScript-Zeichenfolgen in ArrayBuffers und umgekehrt? Insbesondere möchte ich in der Lage sein, den Inhalt eines ArrayBuffers in diesen zu schreiben localStorage
und ihn zurückzulesen.
264
Int8Array
ArrayBufferView
es möglicherweise möglich, einfach die Klammernotation zum Kopieren von Zeichen zu verwendenstring[i] = buffer[i]
und umgekehrt.Uint16Array
s für die 16-Bit-Zeichen von JS), aber JavaScript-Zeichenfolgen sind unveränderlich, sodass Sie einer Zeichenposition nicht direkt zuweisen können. Ich würde immer noch brauchen zu kopierenString.fromCharCode(x)
in der jeder WertUint16Array
zu einem normalenArray
und rufen Sie dann.join()
auf dieArray
.string += String.fromCharCode(buffer[i]);
. Es scheint seltsam, dass es keine integrierten Methoden zum Konvertieren zwischen Zeichenfolgen und typisierten Arrays gibt. Sie mussten wissen, dass so etwas auftauchen würde.Antworten:
Update 2016 - Fünf Jahre später gibt es jetzt neue Methoden in den Spezifikationen (siehe Unterstützung unten), um zwischen Zeichenfolgen und typisierten Arrays unter Verwendung der richtigen Codierung zu konvertieren.
TextEncoder
Das
TextEncoder
stellt dar :Änderungsnotiz, da oben geschrieben wurde: (ibid.)
*) Aktualisierte Spezifikationen (W3) und hier (whatwg).
Nach dem Erstellen einer Instanz von
TextEncoder
wird eine Zeichenfolge verwendet und mit einem bestimmten Codierungsparameter codiert:Sie verwenden dann natürlich den
.buffer
Parameter für das ErgebnisUint8Array
, um die UnterlageArrayBuffer
bei Bedarf in eine andere Ansicht zu konvertieren .Stellen Sie einfach sicher, dass die Zeichen in der Zeichenfolge dem Codierungsschema entsprechen. Wenn Sie beispielsweise im Beispiel Zeichen außerhalb des UTF-8-Bereichs verwenden, werden diese in zwei Bytes anstelle von einem codiert.
Für den allgemeinen Gebrauch würden Sie die UTF-16-Codierung für Dinge wie verwenden
localStorage
.TextDecoder
Ebenso kann der umgekehrte Prozess verwendet die
TextDecoder
:Alle verfügbaren Dekodierungsarten finden Sie hier .
Die MDN StringView-Bibliothek
Eine Alternative zu diesen ist die Verwendung der
StringView
Bibliothek (lizenziert als lgpl-3.0). Das Ziel ist:viel mehr Flexibilität geben. Es würde jedoch erfordern, dass wir eine Verknüpfung zu dieser Bibliothek herstellen oder diese einbetten, während
TextEncoder
/TextDecoder
in modernen Browsern integriert ist.Unterstützung
Stand Juli / 2018:
TextEncoder
(Experimentell, auf Standardstrecke)quelle
var encoder = 'TextEncoder' in window ? new TextEncoder() : {encode: function(str){return Uint8Array.from(str, function(c){return c.codePointAt(0);});}};
so können Sie nurvar array = encoder.encode('hello');
TextEncoder
ist, dass, wenn Sie Binärdaten in einer Zeichenfolge (wie Bild) haben, Sie nicht verwenden möchtenTextEncoder
(anscheinend). Zeichen mit Codepunkten größer als 127 erzeugen zwei Bytes. Warum habe ich Binärdaten in einer Zeichenfolge?cy.fixture(NAME, 'binary')
(cypress
) erzeugt einen String.Obwohl Dennis und Gengkev Lösungen für die Verwendung von Blob / FileReader funktionieren, würde ich diesen Ansatz nicht empfehlen. Es ist ein asynchroner Ansatz für ein einfaches Problem und viel langsamer als eine direkte Lösung. Ich habe einen Beitrag in html5rocks mit einer einfacheren und (viel schnelleren) Lösung verfasst: http://updates.html5rocks.com/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
Und die Lösung ist:
BEARBEITEN:
Die Codierungs-API hilft bei der Lösung des Problems der Zeichenfolgenkonvertierung . Lesen Sie die Antwort von Jeff Posnik auf Html5Rocks.com auf den obigen Originalartikel.
Auszug:
quelle
This is a cool text!
20 Byte in UTF8 - 40 Byte in Unicode. (2)ÄÖÜ
6 Bytes in UTF8 - 6 Bytes in Unicode. (3)☐☑☒
9 Bytes in UTF8 - 6 Bytes in Unicode. Wenn Sie die Zeichenfolge als UTF8-Datei speichern möchten (über Blob und File Writer API), können Sie diese beiden Methoden nicht verwenden, da sich der ArrayBuffer in Unicode und nicht in UTF8 befindet.String.fromCharCode.apply(null, new Uint16Array(new ArrayBuffer(246300))).length
funktioniert für mich in Chrome, aber wenn Sie stattdessen 246301 verwenden, erhalte ich Ihre RangeError-AusnahmeSie können
TextEncoder
undTextDecoder
aus dem Codierungsstandard , der von der Stringencodierungsbibliothek mehrfach gefüllt wird, verwenden, um Zeichenfolgen in und aus ArrayBuffers zu konvertieren:quelle
npm install text-encoding
,var textEncoding = require('text-encoding'); var TextDecoder = textEncoding.TextDecoder;
. Nein Danke.Blob ist viel langsamer als
String.fromCharCode(null,array);
Dies schlägt jedoch fehl, wenn der Array-Puffer zu groß wird. Die beste Lösung, die ich gefunden habe, besteht darin, sie zu verwenden
String.fromCharCode(null,array);
und in Vorgänge aufzuteilen, die den Stapel nicht sprengen, aber schneller als jeweils ein Zeichen sind.Die beste Lösung für große Array-Puffer ist:
Ich fand das ungefähr 20 Mal schneller als mit Blob. Es funktioniert auch für große Strings von über 100 MB.
quelle
Basierend auf der Antwort von gengkev habe ich Funktionen für beide Arten erstellt, da BlobBuilder String und ArrayBuffer verarbeiten kann:
und
Ein einfacher Test:
quelle
a[y * w + x] = (x + y) / 2 * 16;
versucht berechnet wurdengetBlob("x")
, mit vielen verschiedenen Mimetypen - kein Glück.new BlobBuilder(); bb.append(buf);
zunew Blob([buf])
, wandeln Sie den ArrayBuffer in der zweiten Funktion übernew UintArray(buf)
(oder was auch immer für den zugrunde liegenden Datentyp geeignet ist) in ein UintArray um und entfernen Sie dann diegetBlob()
Aufrufe. Um die Sauberkeit zu gewährleisten, benennen Sie bb in blob um, da es kein BlobBuilder mehr ist.Im Folgenden geht es darum, binäre Zeichenfolgen aus Array-Puffern abzurufen
Ich würde empfehlen, nicht zu verwenden
weil es
Maximum call stack size exceeded
Fehler beim 120000-Byte-Puffer (Chrome 29) erhalten)Wenn Sie genau eine synchrone Lösung benötigen, verwenden Sie so etwas wie
Es ist so langsam wie das vorherige, funktioniert aber korrekt. Es scheint, dass es zum Zeitpunkt des Schreibens keine recht schnelle synchrone Lösung für dieses Problem gibt (alle in diesem Thema erwähnten Bibliotheken verwenden denselben Ansatz für ihre synchronen Funktionen).
Aber was ich wirklich empfehle, ist die Verwendung von
Blob
+FileReader
AnsatzDer einzige Nachteil (nicht für alle) ist, dass es asynchron ist . Und es ist ungefähr 8-10 mal schneller als frühere Lösungen! (Einige Details: Die synchrone Lösung in meiner Umgebung benötigte 950-1050 ms für einen 2,4-MB-Puffer, aber die Lösung mit FileReader hatte Zeiten von etwa 100-120 ms für dieselbe Datenmenge. Und ich habe beide synchronen Lösungen auf einem 100-KB-Puffer getestet und sie haben gedauert fast zur gleichen Zeit, also ist die Schleife nicht viel langsamer als die Verwendung von 'anwenden'.)
Übrigens hier: Wie man ArrayBuffer in und von einem String- Autor konvertiert, vergleicht zwei Ansätze wie mich und erhält völlig entgegengesetzte Ergebnisse ( sein Testcode ist hier ). Warum so unterschiedliche Ergebnisse? Wahrscheinlich wegen seiner Testzeichenfolge, die 1 KB lang ist (er nannte sie "veryLongStr"). Mein Puffer war ein wirklich großes JPEG-Bild mit einer Größe von 2,4 MB.
quelle
( Update Bitte lesen Sie die 2. Hälfte dieser Antwort, in der ich (hoffentlich) eine vollständigere Lösung bereitgestellt habe.)
Ich bin auch auf dieses Problem gestoßen, das folgende funktioniert für mich in FF 6 (für eine Richtung):
Leider erhalten Sie natürlich eher ASCII-Textdarstellungen der Werte im Array als Zeichen. Es ist (sollte) immer noch viel effizienter als eine Schleife. z.B. Für das obige Beispiel ist das Ergebnis
0004000000
eher als mehrere Nullzeichen & ein chr (4).Bearbeiten:
Nachdem Sie sich hier MDC angesehen haben , können Sie ein
ArrayBuffer
aus einemArray
der folgenden erstellen :Um Ihre ursprüngliche Frage zu beantworten, können Sie
ArrayBuffer
<->String
wie folgt konvertieren :Der Einfachheit halber ist hier ein
function
zum Konvertieren eines rohen UnicodesString
in einenArrayBuffer
(funktioniert nur mit ASCII / Ein-Byte-Zeichen)Mit den obigen Anweisungen können Sie von
ArrayBuffer
->String
& zurück zuArrayBuffer
erneut gehen, wo die Zeichenfolge in z..localStorage
:) :)Hoffe das hilft,
Dan
quelle
Im Gegensatz zu den hier aufgeführten Lösungen musste ich in / von UTF-8-Daten konvertieren. Zu diesem Zweck habe ich die folgenden zwei Funktionen mit dem Trick (un) Escape / (en) decodeURIComponent codiert. Sie verschwenden ziemlich viel Speicher und weisen die 9-fache Länge des codierten utf8-Strings zu, obwohl diese von gc wiederhergestellt werden sollten. Verwenden Sie sie nur nicht für 100-MB-Text.
Überprüfen, ob es funktioniert:
quelle
Falls Sie Binärdaten in einer Zeichenfolge haben (erhalten von
nodejs
+readFile(..., 'binary')
odercypress
+cy.fixture(..., 'binary')
usw.), können Sie diese nicht verwendenTextEncoder
. Es unterstützt nurutf8
. Bytes mit Werten>= 128
werden jeweils in 2 Bytes umgewandelt.ES2015:
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
"ºRFl¶é (÷ LõÎW0 Náò8ìÉPPv \ 0"
quelle
Ich stellte fest, dass ich Probleme mit diesem Ansatz hatte, hauptsächlich weil ich versuchte, die Ausgabe in eine Datei zu schreiben, und sie nicht richtig codiert war. Da JS anscheinend UCS-2-Codierung ( Quelle , Quelle ) verwendet, müssen wir diese Lösung noch einen Schritt weiter ausdehnen. Hier ist meine erweiterte Lösung, die für mich funktioniert.
Ich hatte keine Schwierigkeiten mit generischem Text, aber wenn es sich um Arabisch oder Koreanisch handelte, enthielt die Ausgabedatei nicht alle Zeichen, sondern zeigte stattdessen Fehlerzeichen
Dateiausgabe:
","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}
Ich habe die Informationen aus Dennis 'Lösung genommen und diesen Beitrag gefunden.
Hier ist mein Code:
Dadurch kann ich den Inhalt ohne Codierungsprobleme in einer Datei speichern.
So funktioniert es: Grundsätzlich werden die einzelnen 8-Byte-Blöcke, aus denen ein UTF-8-Zeichen besteht, als einzelne Zeichen gespeichert (daher kann ein auf diese Weise erstelltes UTF-8-Zeichen aus 1 bis 4 dieser Zeichen bestehen). UTF-8 codiert Zeichen in einem Format mit einer Länge von 1 bis 4 Byte. Was wir hier tun, ist, den Stich in einer URI-Komponente zu codieren und dann diese Komponente zu nehmen und sie in das entsprechende 8-Byte-Zeichen zu übersetzen. Auf diese Weise verlieren wir nicht die Informationen von UTF8-Zeichen, die länger als 1 Byte sind.
quelle
Wenn Sie ein Beispiel für ein großes Array verwendet haben
arr.length=1000000
, können Sie diesen Code verwenden, um Probleme mit dem Stapelrückruf zu vermeidenUmkehrfunktion Mangini Antwort von oben
quelle
Nun, hier ist eine etwas verschlungene Art, dasselbe zu tun:
Bearbeiten: BlobBuilder ist seit langem zugunsten des Blob-Konstruktors veraltet, der nicht existierte, als ich diesen Beitrag zum ersten Mal schrieb. Hier ist eine aktualisierte Version. (Und ja, das war schon immer eine sehr dumme Art, die Konvertierung durchzuführen, aber es war nur zum Spaß!)
quelle
Nach dem Spielen mit Mangini-Lösung zur Umwandlung von
ArrayBuffer
bisString
-ab2str
(das ist die eleganteste und nützlich ist die ich gefunden habe - danke), hatte ich einige Probleme , wenn große Arrays Handhabung. Insbesondere ruft das AufrufenString.fromCharCode.apply(null, new Uint16Array(buf));
einen Fehler auf:arguments array passed to Function.prototype.apply is too large
.Um es zu lösen (Bypass), habe ich beschlossen, die Eingabe
ArrayBuffer
in Blöcken zu behandeln. Die modifizierte Lösung lautet also:Die Blockgröße ist festgelegt,
2^16
da dies die Größe war, die ich in meiner Entwicklungslandschaft gefunden habe. Das Einstellen eines höheren Werts führte dazu, dass derselbe Fehler erneut auftrat. Sie kann geändert werden, indem Sie dieCHUNK_SIZE
Variable auf einen anderen Wert setzen. Es ist wichtig, eine gerade Zahl zu haben.Hinweis zur Leistung - Ich habe für diese Lösung keine Leistungstests durchgeführt. Da es jedoch auf der vorherigen Lösung basiert und große Arrays verarbeiten kann, sehe ich keinen Grund, es nicht zu verwenden.
quelle
Siehe hier: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays/StringView (eine C-ähnliche Schnittstelle für Zeichenfolgen basierend auf der JavaScript ArrayBuffer-Schnittstelle)
quelle
quelle
arrayBufferToString(stringToArrayBuffer('🐴'))==='44'
Für node.js und auch für Browser, die https://github.com/feross/buffer verwenden
Hinweis: Die Lösungen hier haben bei mir nicht funktioniert. Ich muss node.js und Browser unterstützen und UInt8Array einfach in eine Zeichenfolge serialisieren. Ich könnte es als Nummer [] serialisieren, aber das nimmt unnötigen Platz ein. Mit dieser Lösung muss ich mich nicht um Codierungen kümmern, da es base64 ist. Nur für den Fall, dass andere Leute mit dem gleichen Problem zu kämpfen haben ... Meine zwei Cent
quelle
Angenommen, Sie haben einen arrayBuffer binaryStr:
und dann ordnen Sie den Text dem Staat zu.
quelle
Die "native" Binärzeichenfolge, die atob () zurückgibt, ist ein Array mit 1 Byte pro Zeichen.
Wir sollten also keine 2 Bytes in einem Zeichen speichern.
quelle
Ja:
quelle
Ich würde empfehlen, KEINE veralteten APIs wie BlobBuilder zu verwenden
BlobBuilder ist seit langem vom Blob-Objekt veraltet. Vergleichen Sie den Code in Dennis 'Antwort - wo BlobBuilder verwendet wird - mit dem folgenden Code:
Beachten Sie, wie viel sauberer und weniger aufgebläht dies im Vergleich zur veralteten Methode ist ... Ja, das ist hier definitiv etwas zu beachten.
quelle
Siehe https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/decode
quelle
Ich habe das benutzt und arbeite für mich.
quelle