是否有一种普遍接受的技术可以有效地将JavaScript字符串转换为arraybuffer,反之亦然?具体来说,我希望能够将ArrayBuffer的内容写入localStorage,然后再将其读回来。


当前回答

从emscripten:

function stringToUTF8Array(str, outU8Array, outIdx, maxBytesToWrite) {
  if (!(maxBytesToWrite > 0)) return 0;
  var startIdx = outIdx;
  var endIdx = outIdx + maxBytesToWrite - 1;
  for (var i = 0; i < str.length; ++i) {
    var u = str.charCodeAt(i);
    if (u >= 55296 && u <= 57343) {
      var u1 = str.charCodeAt(++i);
      u = 65536 + ((u & 1023) << 10) | u1 & 1023
    }
    if (u <= 127) {
      if (outIdx >= endIdx) break;
      outU8Array[outIdx++] = u
    } else if (u <= 2047) {
      if (outIdx + 1 >= endIdx) break;
      outU8Array[outIdx++] = 192 | u >> 6;
      outU8Array[outIdx++] = 128 | u & 63
    } else if (u <= 65535) {
      if (outIdx + 2 >= endIdx) break;
      outU8Array[outIdx++] = 224 | u >> 12;
      outU8Array[outIdx++] = 128 | u >> 6 & 63;
      outU8Array[outIdx++] = 128 | u & 63
    } else {
      if (outIdx + 3 >= endIdx) break;
      outU8Array[outIdx++] = 240 | u >> 18;
      outU8Array[outIdx++] = 128 | u >> 12 & 63;
      outU8Array[outIdx++] = 128 | u >> 6 & 63;
      outU8Array[outIdx++] = 128 | u & 63
    }
  }
  outU8Array[outIdx] = 0;
  return outIdx - startIdx
}

使用:

stringToUTF8Array('abs', new Uint8Array(3), 0, 4);

其他回答

Blob比String.fromCharCode(null,array)慢得多;

但如果数组缓冲区太大,就会失败。我发现的最佳解决方案是使用String.fromCharCode(null,数组);并将其拆分为不会破坏堆栈的操作,但每次比单个char更快。

大数组缓冲区的最佳解决方案是:

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;

}

我发现这比使用blob快20倍。它也适用于超过100mb的大字符串。

基于gengkev的回答,我创建了两种方法的函数,因为BlobBuilder可以处理String和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());
}

and

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

一个简单的测试:

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

2016年更新——五年过去了,现在规范中有了新的方法(参见下面的支持),可以使用适当的编码在字符串和类型化数组之间进行转换。

TextEncoder

TextEncoder表示:

TextEncoder接口表示特定方法的编码器, 这是一种特定的字符编码,如utf-8, iso-8859-2, koi8, Cp1261, gbk,…编码器以码点流作为输入 发出一个字节流。

自写上篇以来的变动注:(同上)

注意:Firefox, Chrome和Opera曾经支持编码 utf-8以外的类型(如utf-16、iso-8859-2、koi8、cp1261和 gbk)。Firefox 48[…]], Chrome 54[…]Opera 41,没有 为了匹配,除了utf-8,还有其他编码类型可用 规范。*

*)更新规格(W3)和这里(whatwg)。

在创建TextEncoder实例后,它将接受一个字符串并使用给定的编码参数对其进行编码:

如果(!("TextEncoder"在窗口)) alert(“对不起,这个浏览器不支持TextEncoder…”); var enc = new TextEncoder();// always utf-8 console.log(内附。encode("这是一个转换为Uint8Array的字符串"));

当然,如果需要的话,你可以在结果的Uint8Array上使用.buffer参数来将底层的ArrayBuffer转换为不同的视图。

只需确保字符串中的字符符合编码模式,例如,如果在示例中使用UTF-8范围之外的字符,它们将被编码为两个字节而不是一个字节。

一般情况下,你可以使用UTF-16编码来处理localStorage之类的东西。

TextDecoder

同样,相反的进程使用TextDecoder:

TextDecoder接口表示特定方法的解码器, 这是一种特定的字符编码,如utf-8, iso-8859-2, koi8, Cp1261, gbk,…解码器接受字节流作为输入并发出 代码点流。

所有可用的解码类型都可以在这里找到。

如果(!(“TextDecoder”在窗口)) alert(“抱歉,这个浏览器不支持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、65114114、97121、99111110118101114116, 101100, 32116111, 97, 32115116114105110103]); console.log (enc.decode (arr));

MDN StringView库

另一种选择是使用StringView库(许可为lgpl-3.0),其目标是:

to create a C-like interface for strings (i.e., an array of character codes — an ArrayBufferView in JavaScript) based upon the JavaScript ArrayBuffer interface to create a highly extensible library that anyone can extend by adding methods to the object StringView.prototype to create a collection of methods for such string-like objects (since now: stringViews) which work strictly on arrays of numbers rather than on creating new immutable JavaScript strings to work with Unicode encodings other than JavaScript's default UTF-16 DOMStrings

给予更多的灵活性。然而,它需要我们链接到或嵌入这个库,而TextEncoder/TextDecoder是内置在现代浏览器中。

支持

截至2018年7月:

TextEncoder(实验性,在标准轨道上)

 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

如果你在字符串中有二进制数据(从nodejs + readFile(…, 'binary'),或cypress + cy.fixture(…, 'binary'),等等),你不能使用TextEncoder。它只支持utf8。值为>= 128的字节被转换为2个字节。

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、152、118、92、144、48]

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

“ºRFl¶é(÷LõÎW0 Náò8ìÉPPv\0”

使用splat unpacking代替loops:

arrbuf =新uint8阵列([104、101、108、108、111]) text = String.fromCharCode(.. arrbuf) console . log(文本)

对于子字符串,可以使用arrbuf.slice()。