Existe-t-il une technique communément acceptée pour convertir efficacement des chaînes JavaScript en ArrayBuffers et vice-versa? Plus précisément, j'aimerais pouvoir écrire le contenu d'un ArrayBuffer localStorage
et le relire.
265
Existe-t-il une technique communément acceptée pour convertir efficacement des chaînes JavaScript en ArrayBuffers et vice-versa? Plus précisément, j'aimerais pouvoir écrire le contenu d'un ArrayBuffer localStorage
et le relire.
Int8Array
ArrayBufferView
il pourrait être possible d'utiliser simplement la notation des crochets pour copier les caractèresstring[i] = buffer[i]
et vice versa.Uint16Array
s pour les caractères 16 bits de JS), mais les chaînes JavaScript sont immuables, vous ne pouvez donc pas attribuer directement à une position de caractère. J'aurais encore besoin de copierString.fromCharCode(x)
chaque valeur de laUint16Array
dans une normaleArray
puis d'appeler.join()
laArray
.string += String.fromCharCode(buffer[i]);
. Il semble étrange qu'il n'y aurait pas de méthodes intégrées pour convertir entre les chaînes et les tableaux typés. Ils devaient savoir que quelque chose comme ça arriverait.Réponses:
Mise à jour 2016 - cinq ans plus tard, il y a maintenant de nouvelles méthodes dans les spécifications (voir le support ci-dessous) pour convertir entre les chaînes et les tableaux typés en utilisant un encodage approprié.
TextEncoder
Le
TextEncoder
représente :Modifier la note depuis la rédaction de ce qui précède: (ibid.)
*) Spécifications mises à jour (W3) et ici (whatwg).
Après avoir créé une instance de,
TextEncoder
il prendra une chaîne et l'encodera en utilisant un paramètre d'encodage donné:Vous utilisez alors bien sûr le
.buffer
paramètre sur le résultatUint8Array
pour convertir la sous-coucheArrayBuffer
en une vue différente si nécessaire.Assurez-vous simplement que les caractères de la chaîne adhèrent au schéma de codage, par exemple, si vous utilisez des caractères en dehors de la plage UTF-8 dans l'exemple, ils seront codés sur deux octets au lieu d'un.
Pour une utilisation générale, vous utiliseriez le codage UTF-16 pour des choses comme
localStorage
.TextDecoder
De même, le processus inverse utilise
TextDecoder
:Tous les types de décodage disponibles peuvent être trouvés ici .
La bibliothèque MDN StringView
Une alternative à ceux-ci est d'utiliser la
StringView
bibliothèque (sous licence lgpl-3.0) dont le but est:donnant beaucoup plus de flexibilité. Cependant, cela nous obligerait à créer un lien vers ou à intégrer cette bibliothèque pendant que
TextEncoder
/TextDecoder
est intégré dans les navigateurs modernes.Soutien
En juillet / 2018:
TextEncoder
(Expérimental, sur voie standard)la source
var encoder = 'TextEncoder' in window ? new TextEncoder() : {encode: function(str){return Uint8Array.from(str, function(c){return c.codePointAt(0);});}};
pour que vous puissiez simplementvar array = encoder.encode('hello');
TextEncoder
c'est que si vous avez des données binaires dans une chaîne (comme, image), vous ne voulez pas utiliserTextEncoder
(apparemment). Les caractères avec des points de code supérieurs à 127 produisent deux octets. Pourquoi ai-je des données binaires dans une chaîne?cy.fixture(NAME, 'binary')
(cypress
) produit une chaîne.Bien que les solutions Dennis et gengkev d'utilisation de Blob / FileReader fonctionnent, je ne suggérerais pas d'adopter cette approche. Il s'agit d'une approche asynchrone d'un problème simple, et elle est beaucoup plus lente qu'une solution directe. J'ai fait un post dans html5rocks avec une solution plus simple et (beaucoup plus rapide): http://updates.html5rocks.com/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
Et la solution est:
ÉDITER:
L' API d'encodage permet de résoudre le problème de conversion des chaînes . Découvrez la réponse de Jeff Posnik sur Html5Rocks.com à l'article original ci-dessus.
Extrait:
la source
This is a cool text!
20 octets en UTF8 - 40 octets en Unicode. (2)ÄÖÜ
6 octets en UTF8 - 6 octets en Unicode. (3)☐☑☒
9 octets en UTF8 - 6 octets en Unicode. Si vous souhaitez stocker la chaîne en tant que fichier UTF8 (via l'API Blob et File Writer), vous ne pouvez pas utiliser ces 2 méthodes, car l'ArrayBuffer sera en Unicode et non en UTF8.String.fromCharCode.apply(null, new Uint16Array(new ArrayBuffer(246300))).length
fonctionne pour moi dans Chrome, mais si vous utilisez 246301 à la place, j'obtiens votre exception RangeErrorVous pouvez utiliser
TextEncoder
et àTextDecoder
partir de la norme Encoding , qui est remplie par la bibliothèque stringencoding , pour convertir la chaîne vers et depuis ArrayBuffers:la source
npm install text-encoding
,var textEncoding = require('text-encoding'); var TextDecoder = textEncoding.TextDecoder;
. Non merci.Blob est beaucoup plus lent que
String.fromCharCode(null,array);
mais cela échoue si le tampon du tableau devient trop grand. La meilleure solution que j'ai trouvée est de l'utiliser
String.fromCharCode(null,array);
et de la diviser en opérations qui ne feront pas exploser la pile, mais sont plus rapides qu'un seul caractère à la fois.La meilleure solution pour un grand tampon de tableau est:
J'ai trouvé que c'était environ 20 fois plus rapide que d'utiliser un blob. Il fonctionne également pour les grandes chaînes de plus de 100 Mo.
la source
Sur la base de la réponse de gengkev, j'ai créé des fonctions dans les deux sens, car BlobBuilder peut gérer String et ArrayBuffer:
et
Un test simple:
la source
a[y * w + x] = (x + y) / 2 * 16;
j'ai essayégetBlob("x")
, avec de nombreux mimetypes différents - pas de chance.new BlobBuilder(); bb.append(buf);
parnew Blob([buf])
, transtypez ArrayBuffer dans la deuxième fonction en un UintArray vianew UintArray(buf)
(ou tout ce qui convient au type de données sous-jacent), puis supprimez lesgetBlob()
appels. Enfin, pour la propreté, renommez bb en blob car ce n'est plus un BlobBuilder.Tout ce qui suit concerne l'obtention de chaînes binaires à partir de tampons de tableau
Je recommanderais de ne pas utiliser
parce qu'il
Maximum call stack size exceeded
erreur sur le tampon de 120000 octets (Chrome 29))Si vous avez exactement besoin d'une solution synchrone, utilisez quelque chose comme
il est aussi lent que le précédent mais fonctionne correctement. Il semble qu'au moment d'écrire ceci, il n'y a pas de solution synchrone assez rapide pour ce problème (toutes les bibliothèques mentionnées dans cette rubrique utilisent la même approche pour leurs fonctionnalités synchrones).
Mais ce que je recommande vraiment, c'est d'utiliser l' approche
Blob
+FileReader
le seul inconvénient (pas pour tous) est qu'il est asynchrone . Et c'est environ 8 à 10 fois plus rapide que les solutions précédentes! (Quelques détails: la solution synchrone sur mon environnement a pris 950-1050 ms pour un tampon de 2,4 Mo, mais la solution avec FileReader avait des temps d'environ 100-120 ms pour la même quantité de données. Et j'ai testé les deux solutions synchrones sur un tampon de 100 Ko et elles ont pris presque en même temps, donc la boucle n'est pas beaucoup plus lente en utilisant 'appliquer'.)
BTW ici: Comment convertir ArrayBuffer vers et depuis String auteur compare deux approches comme moi et obtient des résultats complètement opposés ( son code de test est ici ) Pourquoi des résultats si différents? Probablement à cause de sa chaîne de test longue de 1 Ko (il l'a appelée "veryLongStr"). Mon tampon était une très grande image JPEG de 2,4 Mo.
la source
( Mise à jour Veuillez consulter la deuxième moitié de cette réponse, où j'ai (espérons-le) fourni une solution plus complète.)
J'ai également rencontré ce problème, les travaux suivants pour moi dans FF 6 (pour une direction):
Malheureusement, bien sûr, vous vous retrouvez avec des représentations textuelles ASCII des valeurs du tableau, plutôt que des caractères. Cependant, il est (devrait être) beaucoup plus efficace qu'une boucle. par exemple. Pour l'exemple ci-dessus, le résultat est
0004000000
, plutôt que plusieurs caractères nuls et un chr (4).Éditer:
Après avoir regardé MDC ici , vous pouvez créer un à
ArrayBuffer
partir d'unArray
comme suit:Pour répondre à votre question d'origine, cela vous permet de convertir
ArrayBuffer
<->String
comme suit:Pour plus de commodité, voici un
function
pour convertir un Unicode brutString
en unArrayBuffer
(ne fonctionnera qu'avec des caractères ASCII / un octet)Ce qui précède vous permet de passer de
ArrayBuffer
->String
& back àArrayBuffer
nouveau, où la chaîne peut être stockée par exemple..localStorage
:)J'espère que cela t'aides,
Dan
la source
Contrairement aux solutions ici, j'avais besoin de convertir vers / à partir de données UTF-8. À cet effet, j'ai codé les deux fonctions suivantes, en utilisant l'astuce (un) escape / (en) decodeURIComponent. Ils gaspillent assez de mémoire, allouant 9 fois la longueur de la chaîne utf8 encodée, bien que ceux-ci devraient être récupérés par gc. Ne les utilisez tout simplement pas pour du texte de 100 Mo.
Vérifier que cela fonctionne:
la source
Dans le cas où vous avez des données binaires dans une chaîne (obtenues à partir de
nodejs
+readFile(..., 'binary')
, oucypress
+cy.fixture(..., 'binary')
, etc.), vous ne pouvez pas utiliserTextEncoder
. Il prend en charge uniquementutf8
. Les octets avec des valeurs>= 128
sont chacun transformés en 2 octets.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"
la source
J'ai trouvé que j'avais des problèmes avec cette approche, essentiellement parce que j'essayais d'écrire la sortie dans un fichier et qu'elle n'était pas encodée correctement. Étant donné que JS semble utiliser le codage UCS-2 ( source , source ), nous devons étendre cette solution un peu plus loin, voici ma solution améliorée qui fonctionne pour moi.
Je n'ai eu aucune difficulté avec le texte générique, mais lorsqu'il était en arabe ou en coréen, le fichier de sortie n'avait pas tous les caractères mais montrait à la place des caractères d'erreur
Sortie de fichier:
","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}
J'ai pris les informations de la solution de dennis et ce poste que j'ai trouvé.
Voici mon code:
Cela me permet d'enregistrer le contenu dans un fichier sans problème d'encodage.
Comment cela fonctionne: Il prend essentiellement les morceaux de 8 octets simples composant un caractère UTF-8 et les enregistre en tant que caractères uniques (par conséquent, un caractère UTF-8 construit de cette manière peut être composé de 1 à 4 de ces caractères). UTF-8 code les caractères dans un format variant de 1 à 4 octets. Ce que nous faisons ici, c'est coder la piqûre dans un composant URI, puis prendre ce composant et le traduire dans le caractère 8 octets correspondant. De cette façon, nous ne perdons pas les informations fournies par les caractères UTF8 de plus d'un octet de long.
la source
si vous avez utilisé un exemple de tableau énorme,
arr.length=1000000
vous pouvez utiliser ce code pour éviter les problèmes de rappel de pilefonction inverse mangini réponse du haut
la source
Eh bien, voici une façon quelque peu compliquée de faire la même chose:
Edit: BlobBuilder a longtemps été déconseillé en faveur du constructeur Blob, qui n'existait pas lorsque j'ai écrit ce post pour la première fois. Voici une version mise à jour. (Et oui, cela a toujours été une façon très stupide de faire la conversion, mais c'était juste pour le plaisir!)
la source
la source
arrayBufferToString(stringToArrayBuffer('🐴'))==='44'
Après avoir joué avec la solution de mangini pour la conversion de
ArrayBuffer
versString
-ab2str
(qui est la plus élégante et utile que j'ai trouvée - merci!), J'ai eu quelques problèmes lors de la manipulation de grands tableaux. Plus précisément, l'appelString.fromCharCode.apply(null, new Uint16Array(buf));
génère une erreur:arguments array passed to Function.prototype.apply is too large
.Afin de le résoudre (contournement), j'ai décidé de gérer l'entrée
ArrayBuffer
en morceaux. La solution modifiée est donc:La taille de bloc est définie sur
2^16
car c'est la taille que j'ai trouvée pour fonctionner dans mon paysage de développement. La définition d'une valeur plus élevée a provoqué la même erreur. Il peut être modifié en définissant laCHUNK_SIZE
variable sur une valeur différente. Il est important d'avoir un nombre pair.Remarque sur les performances - Je n'ai effectué aucun test de performances pour cette solution. Cependant, comme il est basé sur la solution précédente et peut gérer de grands tableaux, je ne vois aucune raison de ne pas l'utiliser.
la source
Voir ici: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays/StringView (une interface de type C pour les chaînes basées sur l'interface JavaScript ArrayBuffer)
la source
Pour node.js et également pour les navigateurs utilisant https://github.com/feross/buffer
Remarque: Les solutions ici n'ont pas fonctionné pour moi. Je dois prendre en charge node.js et les navigateurs et simplement sérialiser UInt8Array en une chaîne. Je pourrais le sérialiser en nombre [] mais cela occupe un espace inutile. Avec cette solution, je n'ai pas à me soucier des encodages car c'est en base64. Juste au cas où d'autres personnes auraient du mal avec le même problème ... Mes deux cents
la source
Disons que vous avez un arrayBuffer binaryStr:
puis vous affectez le texte à l'état.
la source
La chaîne binaire "native" renvoyée par atob () est un tableau de 1 octet par caractère.
Nous ne devons donc pas stocker 2 octets dans un caractère.
la source
Oui:
la source
Je ne recommanderais PAS d'utiliser des API obsolètes comme BlobBuilder
BlobBuilder est depuis longtemps déconseillé par l'objet Blob. Comparez le code dans la réponse de Dennis - où BlobBuilder est utilisé - avec le code ci-dessous:
Notez à quel point c'est plus propre et moins gonflé par rapport à la méthode obsolète ... Oui, c'est certainement quelque chose à considérer ici.
la source
Voir https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/decode
la source
J'ai utilisé cela et travaille pour moi.
la source