Dizeler ve ArrayBuffers arasında dönüştürme


264

JavaScript dizelerini ArrayBuffers'a verimli bir şekilde dönüştürmek için yaygın olarak kabul edilen bir teknik var mı ? Özellikle, bir ArrayBuffer içeriğini yazmak localStorageve geri okumak istiyorum.


1
Bu konuda herhangi bir deneyimim yok, ancak bir API oluşturursanız API belgelerinden ( khronos.org/registry/typedarray/specs/latest ) bakarsak, Int8Array ArrayBufferViewkarakterleri kopyalamak için dirsek notasyonunu kullanmak mümkün olabilir string[i] = buffer[i].
FK82

2
@ FK82, makul bir yaklaşım gibi görünür ( Uint16ArrayJS'nin 16 bit karakterleri için s'yi kullanır ), ancak JavaScript dizeleri değiştirilemez, bu nedenle doğrudan bir karakter konumuna atayamazsınız. Hala String.fromCharCode(x)her değerin Uint16Arraynormal bir kopyalamak Arrayve sonra çağırmak .join()gerekir Array.
kpozin

@kpozin: Doğru, bunu gerçekten düşünmemiştim.
FK82

5
@kpozin Çoğu modern JS motorunun dize birleştirmesini sadece daha ucuz olduğu noktaya kadar optimize ettiği ortaya çıktı string += String.fromCharCode(buffer[i]);. Dizeler ve yazılan diziler arasında dönüştürme için yerleşik yöntemlerin bulunmaması tuhaf görünmektedir. Böyle bir şeyin ortaya çıkacağını bilmek zorundaydılar.
indir

arrayBuffer.toString () benim için iyi çalışıyor.
citizen conn

Yanıtlar:


128

Güncelleme 2016 - Beş yıl sonra, uygun kodlama kullanarak dizeler ve yazılan diziler arasında dönüştürme yapmak için spesifikasyonlarda yeni yöntemler (aşağıdaki desteğe bakın) var.

TextEncoder

TextEncodertemsil eder :

TextEncoderArayüzü gibi, belirli bir karakter kodlamasını olan özel bir yöntemi için bir kodlayıcı, temsil eder utf-8,iso-8859-2, koi8, cp1261, gbk, ... Kodlayıcı girdi olarak bir kod noktası akışı alır ve bir bayt akışı yayar.

Yukarıdakilerin yazıldığından beri notu değiştir: (age)

Not: Firefox, Chrome ve Opera, utf-8 dışındaki kodlama türlerini (utf-16, iso-8859-2, koi8, cp1261 ve gbk gibi) destekliyordu. Firefox 48 [...], Chrome 54 [...] ve Opera 41'den itibaren, spesifikasyonla eşleşmesi için utf-8 dışında başka kodlama türleri yoktur. *

*) Güncellenmiş özellikler (W3) ve burada (whatwg).

Bir örneğini oluşturduktan sonra TextEncoderbir dize alır ve belirli bir kodlama parametresini kullanarak onu kodlar:

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"));

Daha sonra, gerekirse alt tabakayı farklı bir görünüme dönüştürmek için .buffersonuçtaki parametreyi kullanırsınız .Uint8ArrayArrayBuffer

Dizedeki karakterlerin kodlama şemasına uyduğundan emin olun, örneğin, örnekte UTF-8 aralığının dışındaki karakterleri kullanırsanız, bunlar bir yerine iki bayta kodlanır.

Genel kullanım için UTF-16 kodlamasını aşağıdaki gibi şeyler için kullanırsınız localStorage.

TextDecoder

Benzer şekilde, zıt süreç şunları kullanırTextDecoder :

TextDecoderArayüzü belirli bir karakter kodlaması gibi özel bir yöntem, bir kod çözücünün temsil eder utf-8, iso-8859-2, koi8, cp1261, gbk, ... bir kod çözücü giriş olarak bayt akışı alır ve kod noktalarına akımı yayar.

Mevcut tüm kod çözme türlerini burada bulabilirsiniz .

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

MDN StringView kitaplığı

Bunlara bir alternatif de StringViewkütüphane kullanmaktır (lgpl-3.0 olarak lisanslanmıştır):

  • ArrayBuffer arabirimini temel alarak dizeler için C benzeri bir arabirim (yani, bir dizi karakter kodu - JavaScript'te bir ArrayBufferView) oluşturmak için
  • StringView.prototype nesnesine yöntem ekleyerek herkesin genişletebileceği oldukça genişletilebilir bir kitaplık oluşturma
  • yeni değişmez JavaScript dizeleri oluşturmak yerine tam olarak sayı dizileri üzerinde çalışan bu tür dize benzeri nesneler için (şu andan beri: stringViews) bir yöntem koleksiyonu oluşturmak için
  • varsayılan UTF-16 DOMStrings dışındaki Unicode kodlamaları ile çalışma

çok daha fazla esneklik sağlar. Ancak, bağlantısını bize gerektiren veya bu kütüphane süre gömmek olur TextEncoder/ TextDecoderdahili ediliyor modern tarayıcılarda.

Destek

Temmuz / 2018 itibarıyla:

TextEncoder (Deneysel, Standart Yolda)

 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



2018-04-18'de Safari Mobile (ios) için destek yok: developer.mozilla.org/en-US/docs/Web/API/TextDecoder
bronz adam

One-liner: var encoder = 'TextEncoder' in window ? new TextEncoder() : {encode: function(str){return Uint8Array.from(str, function(c){return c.codePointAt(0);});}};böylece sadecevar array = encoder.encode('hello');
Yeti

1
Bununla birlikte TextEncoder, bir dizede (örneğin, görüntü) ikili veriler varsa, TextEncoder(görünüşte) kullanmak istemezsiniz . Kod noktası 127'den büyük olan karakterler iki bayt oluşturur. Neden bir dizede ikili verilerim var? cy.fixture(NAME, 'binary')( cypress) bir dize üretir.
x-yuri

176

Dennis ve gengkev Blob / FileReader kullanmanın çözümleri işe yarasa da, bu yaklaşımı benimsemeyi tavsiye etmem. Basit bir probleme zaman uyumsuz bir yaklaşımdır ve doğrudan bir çözümden çok daha yavaştır. Daha basit ve (çok daha hızlı) bir çözümle html5rocks'ta bir yazı yaptım: http://updates.html5rocks.com/2012/06/How-to-convert-ArrayBuffer-to-and-from-String

Ve çözüm:

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

DÜZENLE:

Kodlama API dize dönüştürme çözme yardımcı sorunu. Html5Rocks.com'daki Jeff Posnik'in yukarıdaki orijinal makaleye verdiği yanıta göz atın.

Alıntı:

Kodlama API'sı, çalışmanız gereken birçok standart kodlamadan bağımsız olarak ham baytlar ve yerel JavaScript dizeleri arasında çeviri yapmayı kolaylaştırır.

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

16
Ne yazık ki html5rocks hakkındaki yorumum henüz onaylanmadı. Bu yüzden burada kısa bir cevap. Bence bu doğru bir yol değil çünkü çok sayıda karakteri özlüyorsunuz, özellikle de çoğu sayfa bugün UTF-8 kodlamasında. Bir tarafta, daha özel karakterler için (diyelim Asya dillerinde), charCodeAt işlevi 4 Baytlık bir değer döndürür, böylece bunlar kesilir. Diğer tarafta, basit İngilizce karakterler ArrayBuffer'ı iki kez büyütür (her 1 Bayt karakter için 2 Bayt kullanırsınız). Bir WebSocket üzerinden İngilizce bir metin gönderdiğinizi düşünün, bunun iki kez zamana ihtiyacı olacaktır (gerçek zamanlı ortamda iyi değildir).
Dennis

9
Üç örnek: (1) This is a cool text!UTF8'de 20 Bayt - Unicode'da 40 Bayt. (2) ÄÖÜUTF'de 6 Bayt - Unicode'da 6 Bayt. (3) ☐☑☒UTF8'de 9 Bayt - Unicode'da 6 Bayt. Dizeyi UTF8 dosyası olarak saklamak istiyorsanız (Blob ve File Writer API'sı aracılığıyla), ArrayBuffer UTF8'de değil Unicode'da olacağı için bu 2 yöntemi kullanamazsınız.
Dennis

3
Bir hata alıyorum: Yakalanmamış RangeError: Maksimum çağrı yığını boyutu aşıldı. Sorun ne olabilir?
Jacob

6
@Dennis - JS dizeleri UTF8 (hatta UTF16) değil UCS2 kullanır - yani charCodeAt () her zaman 0 -> 65535 değerleri döndürür. 4 bayt bitmesi gereken herhangi bir UTF-8 kod noktası vekil çiftlerle temsil edilir (bkz. En.wikipedia .org / wiki /… ) - yani iki ayrı 16 bit UCS2 değeri.
broofa

6
@jacob - Hatanın, dizinin uzunluğunda, Apply () yöntemine aktarılabilecek bir sınır olması nedeniyle olduğuna inanıyorum. Örneğin String.fromCharCode.apply(null, new Uint16Array(new ArrayBuffer(246300))).lengthChrome'da benim için çalışıyor, ancak bunun yerine 246301 kullanırsanız, RangeError istisnanızı
alıyorum

71

Sen kullanabilirsiniz TextEncoderve TextDecodergelen Kodlama standardı tarafından polyfilled edilir stringencoding kütüphaneye dönüştürme dizeye, ve ArrayBuffers gelen:

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

2
Bu arada, bu varsayılan olarak Firefox'ta mevcuttur: developer.mozilla.org/en-US/docs/Web/API/TextDecoder.decode
Joel Richard

2
Garip geçici çözümlerden çok daha iyi olan yeni API'ler için beğen!
Tomáš Zato - Monica'yı eski durumuna getir

1
Bu, her tür karakterle çalışmaz.
David

5
npm install text-encoding, var textEncoding = require('text-encoding'); var TextDecoder = textEncoding.TextDecoder;. Hayır teşekkürler.
Evan Hu

grumble ... mevcut bir arraybuffer varsa ben içine bir dize yazmak istiyorum sanırım uint8array almak ve 2 kez kopyalamak zorunda ??
shaunc

40

Damla çok daha yavaş String.fromCharCode(null,array);

ancak dizi arabelleği çok büyürse başarısız olur. Bulduğum en iyi çözüm onu ​​kullanmak String.fromCharCode(null,array);ve yığını patlatmayacak, ancak her seferinde tek bir karakterden daha hızlı işlemlere bölmek.

Büyük dizi arabelleği için en iyi çözüm:

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;

}

Bunu damla kullanmaktan yaklaşık 20 kat daha hızlı buldum. Ayrıca 100mb'nin üzerindeki büyük teller için de çalışır.


3
Bu çözümle devam etmeliyiz. Bu, kabul edilenden daha fazla kullanım durumunu çözdüğü için
sam

24

Gengkev'in cevabına dayanarak, her iki yol için de fonksiyonlar oluşturdum, çünkü BlobBuilder String ve ArrayBuffer'ı işleyebilir:

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

ve

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

Basit bir test:

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"
            }
        )
    }
)

ArrayBuffer2String () 'de, console.log () yerine geri çağırma (...) işlevini mi çağırmak istediniz? Aksi takdirde geri çağrı argümanı kullanılmaz.
Dan Phillimore

Bu gidilecek yol gibi görünüyor - teşekkürler genkev ve Dennis. Bunu başarmak için eşzamanlı bir yol olmadığı aptalca görünüyor, ama ne yapabilirsin ...
kpozin

JavaScript tek iş parçacıklı. Bu nedenle FileReader iki nedenden dolayı eşzamansızdır: (1) (çok büyük) bir dosya yüklerken (daha karmaşık bir uygulama hayal edin) diğer JavaScript'in yürütülmesini engellemez ve (2) UI / Tarayıcıyı engellemez (yaygın sorun) uzun JS kodu ile). Birçok API eşzamansızdır. XMLHttpRequest 2'de bile senkronizasyon kaldırılır.
Dennis

Gerçekten bunun benim için işe yarayacağını umuyordum, ancak dizeden ArrayBuffer'a dönüşüm güvenilir bir şekilde çalışmıyor. Ben 256 değerleri ile bir ArrayBuffer yapıyorum ve uzunluğu 256 ile bir dizeye dönüştürebilirsiniz. Ama sonra bir ArrayBuffer dönüştürmek çalışırsanız - benim ilk ArrayBuffer içeriğine bağlı olarak - 376 öğeleri alıyorum. Benim sorunu yeniden denemek istiyorsanız, ben olarak hesaplanan değerlerle, bir Uint8Array bir 16x16 ızgara olarak benim ArrayBuffer tedavi ediyorum a[y * w + x] = (x + y) / 2 * 16; denedim getBlob("x")çok farklı mime-türü ile, - hiç şans.
Matt Cruikshank

18
BlobBuilder daha yeni tarayıcılarda kullanımdan kaldırıldı. Değişim new BlobBuilder(); bb.append(buf);için new Blob([buf]), üzerinden bir UintArray ikinci işlevinde ArrayBuffer dökme new UintArray(buf)(veya altta yatan veri türü için uygun ne varsa) ve sonra kurtulmak getBlob()aramalar. Son olarak, temizlik için, artık bir BlobBuilder olmadığı için bb'yi blob olarak yeniden adlandırın.
sowbug

18

Aşağıdakilerin tümü dizi arabelleklerinden ikili dizeler almakla ilgilidir

Kullanmamanızı tavsiye ederim

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

Çünkü bu

  1. büyük tamponlarda çöküyor (biri 246300 "sihirli" boyutu hakkında yazdı ama Maximum call stack size exceeded120000 bayt arabelleğinde hata var (Chrome 29))
  2. sahip olduğu gerçekten kötü performans (aşağıya bakınız)

Tam olarak senkronize bir çözüme ihtiyacınız varsa,

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

bir öncekinden daha yavaş ama düzgün çalışıyor. Bunu yazarken, bu sorun için oldukça hızlı bir senkron çözüm yoktur (bu konuda bahsedilen tüm kütüphaneler, senkron özellikleri için aynı yaklaşımı kullanır).

Ama gerçekten önerdiğim Blob+ FileReaderyaklaşımını kullanmak

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

tek dezavantajı (herkes için değil) asenkron olmasıdır . Ve önceki çözümlerden yaklaşık 8-10 kat daha hızlı ! (Bazı ayrıntılar: ortamımdaki senkronize çözüm 2.4Mb tampon için 950-1050 ms sürdü, ancak FileReader ile çözüm aynı miktarda veri için yaklaşık 100-120 ms sürdü. Ve 100Kb tamponda her iki senkron çözümü de test ettim ve neredeyse aynı zamanda, bu yüzden döngü 'uygula' kullanmaktan daha yavaş değildir.)

BTW burada: Nasıl ArrayBuffer dönüştürmek ve String yazar benim gibi iki yaklaşım karşılaştırır ve tamamen zıt sonuçlar elde ( onun test kodu burada ) Neden bu kadar farklı sonuçlar? Muhtemelen 1Kb uzunluğundaki test dizesi nedeniyle ("veryLongStr" olarak adlandırılmıştır). Arabelleğim 2.4 MB boyutunda gerçekten büyük bir JPEG görüntüsü idi.


13

( Güncelleme Lütfen (daha umarım) daha eksiksiz bir çözüm sunduğum bu cevabın 2. yarısına bakınız.)

Ben de bu konuyla karşılaştım, aşağıdaki benim için FF 6'da çalışır (bir yönde):

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

Maalesef, dizideki değerlerin karakterlerden ziyade ASCII metin gösterimleri ile sonuçlanırsınız. Yine de bir döngüden çok daha verimli olmalıdır. Örneğin. Yukarıdaki örnek için, sonuç, 0004000000birkaç boş karakter yerine bir chr (4) olur.

Düzenle:

Burada MDC'ye baktıktan sonra , aşağıdakilerden bir tane oluşturabilirsiniz :ArrayBufferArray

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;

Orijinal sorunuzu cevaplamak için ArrayBuffer<-> Stringaşağıdaki gibi dönüştürmenize izin verir :

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

Kolaylık sağlamak için, functionişlenmemiş bir Unicode'u Stringbir ArrayBuffer(yalnızca ASCII / bir baytlık karakterlerle çalışacak) dönüştürmek için

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 ]);

Yukarıdakiler ArrayBuffer-> String& 'dan ArrayBuffertekrar dizeye gitmenize izin verir . .localStorage:)

Bu yardımcı olur umarım,

Dan


1
Bunun (zaman veya alan açısından) etkili bir yöntem olduğunu düşünmüyorum ve bu ikili verileri depolamanın çok sıradışı bir yoludur.
kpozin

@kpozin: Bildiğim kadarıyla, ikili verileri localStorage'da depolamanın başka bir yolu yok
Dan Phillimore

1
Base64 kodlamasını kullanmaya ne dersiniz?
Nick Sotiros

13

Buradaki çözümlerin aksine, UTF-8 verilerine dönüştürmem gerekiyordu. Bu amaçla, (un) escape / (en) decodeURIComponent hile kullanarak aşağıdaki iki işlevi kodladım. Bunlar, gc tarafından kurtarılsa da, şifrelenmiş utf8-string'in 9 katını ayırarak bellekte oldukça israfa yol açarlar. Sadece 100mb metin için kullanmayın.

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

Çalışıp çalışmadığını kontrol etme:

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

8

Bir dizede ( nodejs+ readFile(..., 'binary')veya cypress+ cy.fixture(..., 'binary'), vb. Elde edilen) ikili verileriniz varsa kullanamazsınız TextEncoder. Sadece destekler utf8. Değerli baytların >= 128her biri 2 bayta dönüştürülür.

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"


7

Temelde bir dosyaya çıktı yazmaya çalışıyorum ve düzgün kodlanmamış olduğundan, bu yaklaşımla ilgili sorunlar yaşadım bulundu. JS, UCS-2 kodlamasını ( kaynak , kaynak ) kullanıyor gibi göründüğünden , bu çözümü bir adım daha uzatmamız gerekiyor, işte benim için çalışan gelişmiş çözümüm.

Genel metinle ilgili hiçbir sorun yaşamadım, ancak Arap veya Korece'ye düştüğünde, çıktı dosyasında tüm karakterlere sahip değil, bunun yerine hata karakterleri gösteriliyordu

Dosya çıktısı: ","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}

Orijinal: ","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}

Bilgileri dennis'in çözümünden aldım ve bu yazıyı buldum.

İşte benim kod:

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

Bu, içeriği kodlamadan bir dosyaya kaydetmeme izin veriyor.

Nasıl çalışır: Temel olarak bir UTF-8 karakteri oluşturan tek 8 baytlık parçaları alır ve bunları tek karakter olarak kaydeder (bu nedenle bu şekilde oluşturulmuş bir UTF-8 karakteri, bu karakterlerin 1-4'ünden oluşabilir). UTF-8, karakterleri 1 ila 4 bayt uzunluğunda değişen bir biçimde kodlar. Burada yaptığımız, bir URI bileşenindeki sokmayı kodlamak ve daha sonra bu bileşeni alıp karşılık gelen 8 baytlık karaktere çevirmektir. Bu şekilde UTF8 karakterleri tarafından 1 bayttan uzun verilen bilgileri kaybetmeyiz.


6

büyük dizi örneği kullandıysanız arr.length=1000000 , yığın geri çağrı sorunlarını önlemek için bu kodu kullanabilirsiniz

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
}

ters fonksiyon mangini üstten cevap

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

4

İşte aynı şeyi yapmanın biraz kıvrımlı bir yolu:

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());

Düzenleme: BlobBuilder uzun zamandır bu yazıyı ilk yazdığımda mevcut olmayan Blob yapıcısı lehine kullanımdan kaldırıldı. İşte güncellenmiş bir sürüm. (Ve evet, bu dönüşüm yapmak için her zaman çok aptalca bir yol oldu, ama sadece eğlence içindi!)

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]));

3

Dan dönüştürmek için MANGINI en solüsyonu ile oynadıktan sonra ArrayBufferhiç String- ab2str(! Bulduğum en şık ve kullanışlı biri olan - teşekkürler) Büyük diziler tutarken, bazı sorunlar vardı. Daha spesifik olarak, çağrı yapmak String.fromCharCode.apply(null, new Uint16Array(buf));bir hata atar:

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

Çözmek için (bypass) girdiyi ArrayBufferparçalar halinde ele almaya karar verdim . Yani değiştirilmiş çözüm:

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

Yığın boyutu, 2^16geliştirme ortamımda çalıştığım boyut olduğu için ayarlandı . Daha yüksek bir değer ayarlamak aynı hatanın yeniden oluşmasına neden oldu. CHUNK_SIZEDeğişken farklı bir değere ayarlanarak değiştirilebilir . Eşit bir sayıya sahip olmak önemlidir.

Performansla ilgili not - Bu çözüm için performans testi yapmadım. Ancak, önceki çözüme dayandığı ve büyük dizileri işleyebildiği için, neden kullanmamanın bir neden göremiyorum.


Kullanabileceğiniz typedarray.subarray belirtilen konum ve boyutta bir parça almak için, bu ı js ikili biçimlerde kapalı başlıklarını okumak için ne olduğunu
Nikos M.


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

dize unicode karakterler içeriyorsa bu kod buggy'dir. örnek:arrayBufferToString(stringToArrayBuffer('🐴'))==='44'
xmcp

2

Node.js ve ayrıca https://github.com/feross/buffer kullanan tarayıcılar için

function ab2str(buf: Uint8Array) {
  return Buffer.from(buf).toString('base64');
}
function str2ab(str: string) {
  return new Uint8Array(Buffer.from(str, 'base64'))
}

Not: Buradaki çözümler benim için işe yaramadı. Düğüm.js ve tarayıcıları desteklemeli ve sadece bir dizeye UInt8Array serileştirmeliyim. Bir sayı [] olarak serileştirebilirdim ama bu gereksiz yer kaplar. Bu çözümle base64 olduğundan kodlamalar konusunda endişelenmem gerekmiyor. Diğer insanların aynı problemle mücadele etmesi durumunda ... İki sentim


2

Diyelim bir arrayBuffer binaryStr var:

let text = String.fromCharCode.apply(null, new Uint8Array(binaryStr));

ve sonra metni duruma atarsınız.


1

Atob () öğesinin döndürdüğü "yerel" ikili dize, karakter başına 1 baytlık bir Dizidir.

Bu yüzden bir karakteri 2 bayt saklamamalıyız.

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

1

Evet:

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

0

BlobBuilder gibi kullanımdan kaldırılmış API'ları KULLANMAYIN

BlobBuilder uzun süredir Blob nesnesi tarafından kullanımdan kaldırıldı. Dennis'in cevabındaki (BlobBuilder'ın kullanıldığı) kodu aşağıdaki kodla karşılaştırın:

function arrayBufferGen(str, cb) {

  var b = new Blob([str]);
  var f = new FileReader();

  f.onload = function(e) {
    cb(e.target.result);
  }

  f.readAsArrayBuffer(b);

}

Bu, kullanımdan kaldırılan yönteme kıyasla ne kadar temiz ve daha az şişirilmiş olduğuna dikkat edin ... Evet, bu kesinlikle dikkate alınması gereken bir şey.


Demek istediğim, evet, ama Blob yapıcısı 2012'de gerçekten kullanılabilir değildi;)
gengkev


0

Bunu kullandım ve benim için çalışıyor.

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;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.