Apakah ada teknik yang diterima secara umum untuk mengkonversi string JavaScript secara efisien ke ArrayBuffers dan sebaliknya? Secara khusus, saya ingin dapat menulis konten ArrayBuffer localStorage
dan membacanya kembali.
264
Apakah ada teknik yang diterima secara umum untuk mengkonversi string JavaScript secara efisien ke ArrayBuffers dan sebaliknya? Secara khusus, saya ingin dapat menulis konten ArrayBuffer localStorage
dan membacanya kembali.
Int8Array
ArrayBufferView
, mungkin saja menggunakan notasi braket untuk menyalin karakterstring[i] = buffer[i]
dan sebaliknya.Uint16Array
s untuk karakter 16-bit JS), tetapi string JavaScript tidak dapat diubah sehingga Anda tidak dapat menetapkan secara langsung ke posisi karakter. Saya masih perlu menyalinString.fromCharCode(x)
dari setiap nilai dalamUint16Array
untuk normalArray
dan kemudian memanggil.join()
padaArray
.string += String.fromCharCode(buffer[i]);
. Tampaknya aneh bahwa tidak akan ada metode bawaan untuk mengkonversi antara string dan array yang diketik. Mereka harus tahu sesuatu seperti ini akan muncul.Jawaban:
Pembaruan 2016 - lima tahun sekarang ada metode baru dalam spesifikasi (lihat dukungan di bawah) untuk mengkonversi antara string dan array yang diketik menggunakan pengkodean yang tepat.
TextEncoder
The
TextEncoder
mewakili :Ubah catatan sejak tulisan di atas ditulis: (ibid.)
*) Spesifikasi yang diperbarui (W3) dan di sini (whatwg).
Setelah membuat instance dari
TextEncoder
itu akan mengambil string dan menyandikannya menggunakan parameter pengkodean yang diberikan:Anda kemudian tentu saja menggunakan
.buffer
parameter pada hasilUint8Array
untuk mengkonversi lapisan bawahArrayBuffer
ke tampilan yang berbeda jika diperlukan.Pastikan saja bahwa karakter dalam string mematuhi skema penyandian, misalnya, jika Anda menggunakan karakter di luar rentang UTF-8 dalam contoh mereka akan dikodekan menjadi dua byte, bukan satu.
Untuk penggunaan umum, Anda akan menggunakan pengkodean UTF-16 untuk hal-hal seperti
localStorage
.TextDecoder
Demikian juga, proses sebaliknya menggunakan
TextDecoder
:Semua tipe decoding yang tersedia dapat ditemukan di sini .
Perpustakaan MDN StringView
Alternatif untuk ini adalah dengan menggunakan
StringView
perpustakaan (berlisensi sebagai lgpl-3.0) yang tujuannya adalah:memberi lebih banyak fleksibilitas. Namun, itu akan mengharuskan kita untuk menautkan atau menyematkan pustaka ini saat
TextEncoder
/TextDecoder
sedang built-in di browser modern.Dukung
Per Juli / 2018:
TextEncoder
(Eksperimental, Di Jalur Standar)sumber
var encoder = 'TextEncoder' in window ? new TextEncoder() : {encode: function(str){return Uint8Array.from(str, function(c){return c.codePointAt(0);});}};
jadi Anda bisavar array = encoder.encode('hello');
TextEncoder
adalah bahwa jika Anda memiliki data biner dalam sebuah string (seperti, gambar), Anda tidak ingin menggunakanTextEncoder
(tampaknya). Karakter dengan titik kode yang lebih besar dari 127 menghasilkan dua byte. Mengapa saya memiliki data biner dalam sebuah string?cy.fixture(NAME, 'binary')
(cypress
) menghasilkan string.Meskipun Dennis dan solusi gengkev menggunakan Blob / FileReader bekerja, saya tidak akan menyarankan mengambil pendekatan itu. Ini adalah pendekatan async untuk masalah sederhana, dan itu jauh lebih lambat daripada solusi langsung. Saya telah membuat posting di html5rocks dengan solusi yang lebih sederhana dan (jauh lebih cepat): http://updates.html5rocks.com/2012/06/Bagaimana- untuk- mengkonversi-ArrayBuffer-to-and-from-String
Dan solusinya adalah:
EDIT:
The API Encoding membantu memecahkan string konversi masalah. Lihat respons dari Jeff Posnik di Html5Rocks.com ke artikel asli di atas.
Kutipan:
sumber
This is a cool text!
20 Byte di UTF8 - 40 Byte di Unicode. (2)ÄÖÜ
6 Bytes di UTF8 - 6 Bytes di Unicode. (3)☐☑☒
9 Bytes di UTF8 - 6 Bytes di Unicode. Jika Anda ingin menyimpan string sebagai file UTF8 (melalui Blob dan File Writer API), Anda tidak dapat menggunakan 2 metode ini, karena ArrayBuffer akan berada di Unicode dan bukan di UTF8.String.fromCharCode.apply(null, new Uint16Array(new ArrayBuffer(246300))).length
bekerja untuk saya di Chrome, tetapi jika Anda menggunakan 246301, saya mendapatkan pengecualian RangeError AndaAnda dapat menggunakan
TextEncoder
danTextDecoder
dari standar Pengkodean , yang diisi dengan polyfilled oleh perpustakaan stringencoding , untuk mengonversi string ke dan dari ArrayBuffers:sumber
npm install text-encoding
,var textEncoding = require('text-encoding'); var TextDecoder = textEncoding.TextDecoder;
. Tidak, terima kasih.Gumpalan jauh lebih lambat daripada
String.fromCharCode(null,array);
tapi itu gagal jika buffer array terlalu besar. Solusi terbaik yang saya temukan adalah menggunakan
String.fromCharCode(null,array);
dan membaginya menjadi operasi yang tidak akan meniup stack, tetapi lebih cepat dari satu karakter sekaligus.Solusi terbaik untuk buffer array besar adalah:
Saya menemukan ini sekitar 20 kali lebih cepat daripada menggunakan gumpalan. Ini juga berfungsi untuk string besar lebih dari 100MB.
sumber
Berdasarkan jawaban gengkev, saya membuat fungsi untuk kedua cara, karena BlobBuilder dapat menangani String dan ArrayBuffer:
dan
Tes sederhana:
sumber
a[y * w + x] = (x + y) / 2 * 16;
saya sudah mencobagetBlob("x")
, dengan banyak mimetipe yang berbeda - tidak beruntung.new BlobBuilder(); bb.append(buf);
kenew Blob([buf])
, masukkan ArrayBuffer di fungsi kedua ke UintArray vianew UintArray(buf)
(atau apa pun yang sesuai untuk tipe data yang mendasarinya), lalu singkirkangetBlob()
panggilan. Akhirnya, untuk kebersihan, ganti nama bb menjadi gumpalan karena itu bukan BlobBuilder lagi.Semua berikut ini adalah tentang mendapatkan string biner dari buffer array
Saya sarankan untuk tidak menggunakan
karena itu
Maximum call stack size exceeded
kesalahan pada buffer 120000 byte (Chrome 29))Jika Anda benar-benar membutuhkan solusi sinkron, gunakan sesuatu seperti
lambat seperti yang sebelumnya tetapi bekerja dengan benar. Tampaknya pada saat penulisan ini tidak ada solusi sinkron yang cukup cepat untuk masalah itu (semua perpustakaan yang disebutkan dalam topik ini menggunakan pendekatan yang sama untuk fitur sinkron mereka).
Tapi yang saya sangat merekomendasikan adalah menggunakan pendekatan
Blob
+FileReader
satu-satunya kelemahan (tidak untuk semua) adalah asinkron . Dan itu sekitar 8-10 kali lebih cepat daripada solusi sebelumnya! (Beberapa perincian: solusi sinkron pada lingkungan saya membutuhkan 950-1050 ms untuk buffer 2,4Mb tetapi solusi dengan FileReader memiliki kali sekitar 100-120 ms untuk jumlah data yang sama. Dan saya telah menguji keduanya solusi sinkron pada buffer 100Kb dan mereka telah mengambil waktu yang hampir bersamaan, sehingga loop tidak jauh lebih lambat menggunakan 'apply'.)
BTW di sini: Bagaimana cara mengubah ArrayBuffer ke dan dari String author membandingkan dua pendekatan seperti saya dan mendapatkan hasil yang benar-benar berlawanan ( kode pengujiannya ada di sini ) Mengapa hasil yang sangat berbeda? Mungkin karena string pengujiannya yang panjangnya 1Kb (ia menyebutnya "veryLongStr"). Buffer saya adalah gambar JPEG yang sangat besar dengan ukuran 2,4 MB.
sumber
( Pembaruan Silakan lihat bagian 2 dari jawaban ini, di mana saya (semoga) memberikan solusi yang lebih lengkap.)
Saya juga mengalami masalah ini, berikut ini berfungsi untuk saya di FF 6 (untuk satu arah):
Sayangnya, tentu saja, Anda berakhir dengan representasi teks ASCII dari nilai-nilai dalam array, bukan karakter. Ini masih (harus) jauh lebih efisien daripada loop. misalnya. Untuk contoh di atas, hasilnya adalah
0004000000
, alih-alih beberapa karakter nol & satu chr (4).Edit:
Setelah melihat MDC di sini , Anda dapat membuat
ArrayBuffer
dariArray
sebagai berikut:Untuk menjawab pertanyaan awal Anda, ini memungkinkan Anda mengonversi
ArrayBuffer
<->String
sebagai berikut:Untuk kenyamanan, berikut adalah
function
untuk mengubah Unicode mentahString
menjadiArrayBuffer
(hanya akan bekerja dengan karakter ASCII / satu byte)Di atas memungkinkan Anda untuk pergi dari
ArrayBuffer
->String
& kembaliArrayBuffer
lagi, di mana string dapat disimpan dalam mis..localStorage
:)Semoga ini membantu,
Dan
sumber
Tidak seperti solusi di sini, saya perlu mengkonversi ke / dari data UTF-8. Untuk tujuan ini, saya mengkodekan dua fungsi berikut, menggunakan (un) escape / (en) decodeURIComponent trick. Mereka cukup boros memori, mengalokasikan 9 kali panjang utf8-string yang dikodekan, meskipun itu harus dipulihkan oleh gc. Hanya saja, jangan menggunakannya untuk teks 100mb.
Memeriksa apakah itu berfungsi:
sumber
Jika Anda memiliki data biner dalam sebuah string (diperoleh dari
nodejs
+readFile(..., 'binary')
, ataucypress
+cy.fixture(..., 'binary')
, dll), Anda tidak dapat menggunakanTextEncoder
. Hanya mendukungutf8
. Bytes dengan nilai>= 128
masing-masing diubah menjadi 2 byte.ES2015:
Uint8Array (33) [2, 134, 140, 186, 82, 70, 108, 182, 233, 40, 143, 247, 29, 76, 245, 206, 29, 87, 48, 160, 78, 78, 225, 242 , 56, 236, 201, 80, 80, 152, 118, 92, 144, 48
"ºRFl¶é (÷ LõÎW0 Náò8ìÉPPv \ 0"
sumber
Saya menemukan saya punya masalah dengan pendekatan ini, pada dasarnya karena saya mencoba untuk menulis output ke file dan itu tidak dikodekan dengan benar. Karena JS tampaknya menggunakan pengkodean UCS-2 ( sumber , sumber ), kita perlu memperluas solusi ini selangkah lebih maju, inilah solusi saya yang ditingkatkan yang bekerja untuk saya.
Saya tidak mengalami kesulitan dengan teks umum, tetapi ketika itu ke bahasa Arab atau Korea, file output tidak memiliki semua karakter tetapi sebaliknya menunjukkan karakter kesalahan
Output file:
","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}
Asli:
","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}
Saya mengambil informasi dari solusi dennis dan posting ini saya temukan.
Ini kode saya:
Ini memungkinkan saya untuk menyimpan konten ke file tanpa masalah penyandian.
Cara kerjanya: Pada dasarnya dibutuhkan potongan 8-byte tunggal yang menyusun karakter UTF-8 dan menyimpannya sebagai karakter tunggal (oleh karena itu karakter UTF-8 yang dibangun dengan cara ini, dapat disusun oleh 1-4 karakter ini). UTF-8 mengkodekan karakter dalam format yang panjangnya bervariasi dari 1 hingga 4 byte. Apa yang kami lakukan di sini adalah mengkodekan sengatan dalam komponen URI dan kemudian mengambil komponen ini dan menerjemahkannya dalam karakter 8 byte yang sesuai. Dengan cara ini kami tidak kehilangan informasi yang diberikan oleh karakter UTF8 yang panjangnya lebih dari 1 byte.
sumber
jika Anda menggunakan contoh array besar
arr.length=1000000
Anda dapat kode ini untuk menghindari masalah panggilan balik stackmembalikkan fungsi mangini menjawab dari atas
sumber
Nah, inilah cara yang agak berbelit-belit untuk melakukan hal yang sama:
Sunting: BlobBuilder telah lama ditinggalkan demi konstruktor Blob, yang tidak ada ketika saya pertama kali menulis posting ini. Ini versi terbaru. (Dan ya, ini selalu menjadi cara yang sangat konyol untuk melakukan konversi, tapi itu hanya untuk bersenang-senang!)
sumber
Setelah bermain dengan solusi mangini untuk mengkonversi dari
ArrayBuffer
keString
-ab2str
(yang merupakan yang paling elegan dan berguna yang saya temukan - terima kasih!), Saya memiliki beberapa masalah ketika menangani array besar. Lebih spesifik, panggilanString.fromCharCode.apply(null, new Uint16Array(buf));
melempar kesalahan:arguments array passed to Function.prototype.apply is too large
.Untuk mengatasinya (memotong) saya telah memutuskan untuk menangani input
ArrayBuffer
dalam potongan. Jadi solusi yang dimodifikasi adalah:Ukuran chunk diatur
2^16
karena ini adalah ukuran yang saya temukan untuk bekerja di lanskap pengembangan saya. Menetapkan nilai yang lebih tinggi menyebabkan kesalahan yang sama terulang kembali. Itu dapat diubah dengan mengaturCHUNK_SIZE
variabel ke nilai yang berbeda. Penting untuk memiliki angka genap.Catatan tentang kinerja - Saya tidak melakukan tes kinerja untuk solusi ini. Namun, karena ini didasarkan pada solusi sebelumnya, dan dapat menangani array yang besar, saya tidak melihat alasan mengapa tidak menggunakannya.
sumber
Lihat di sini: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays/StringView (antarmuka mirip-C untuk string yang didasarkan pada antarmuka JavaScript ArrayBuffer)
sumber
sumber
arrayBufferToString(stringToArrayBuffer('🐴'))==='44'
Untuk node.js dan juga untuk browser menggunakan https://github.com/feross/buffer
Catatan: Solusi di sini tidak bekerja untuk saya. Saya perlu mendukung node.js dan browser dan hanya membuat serial UInt8Array ke string. Saya bisa membuat cerita bersambung sebagai angka [] tapi itu menempati ruang yang tidak perlu. Dengan solusi itu saya tidak perlu khawatir tentang pengkodean karena itu base64. Untuk berjaga-jaga jika orang lain bergumul dengan masalah yang sama ... Dua sen saya
sumber
Katakanlah Anda memiliki arrayBuffer binaryStr:
dan kemudian Anda menetapkan teks ke negara.
sumber
String biner "asli" yang dikembalikan atob () adalah Array 1-byte-per-karakter.
Jadi kita seharusnya tidak menyimpan 2 byte ke dalam karakter.
sumber
Iya:
sumber
Saya sarankan TIDAK menggunakan API yang sudah usang seperti BlobBuilder
BlobBuilder telah lama ditinggalkan oleh objek Blob. Bandingkan kode dalam jawaban Dennis - di mana BlobBuilder digunakan - dengan kode di bawah ini:
Perhatikan seberapa banyak ini lebih bersih dan lebih sedikit kembung dibandingkan dengan metode yang sudah usang ... Ya, ini pasti sesuatu yang perlu dipertimbangkan di sini.
sumber
Lihat https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/decode
sumber
Saya menggunakan ini dan bekerja untuk saya.
sumber