是否有一种普遍接受的技术可以有效地将 JavaScript 字符串转换为ArrayBuffers,反之亦然?具体来说,我希望能够将 ArrayBuffer 的内容写入localStorage
并读回。
在字符串和 ArrayBuffers 之间转换
2016年更新- 五年后,规范中出现了新方法(请参阅下面的支持),可以使用正确的编码在字符串和类型化数组之间进行转换。
文本编码器
的
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 之外没有其他可用的编码类型。*
创建 的实例后,TextEncoder
它将获取一个字符串并使用给定的编码参数对其进行编码:
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"));
如果需要,您当然可以使用.buffer
结果上的参数Uint8Array
将底层转换ArrayBuffer
为不同的视图。
只需确保字符串中的字符符合编码模式,例如,如果您在示例中使用 UTF-8 范围之外的字符,它们将被编码为两个字节而不是一个字节。
对于一般用途,您可以将 UTF-16 编码用于诸如localStorage
.
文本解码器
同样,相反的过程使用TextDecoder
:
该
TextDecoder
接口表示特定方法的解码器,即特定的字符编码,例如utf-8
,iso-8859-2
,koi8
,cp1261
,gbk
, ... 解码器将字节流作为输入并发出代码点流。
所有可用的解码类型都可以在这里找到。
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 库
另一种方法是使用StringView
库(许可为 lgpl-3.0),其目标是:
- 基于 JavaScript ArrayBuffer 接口为字符串(即字符代码数组——JavaScript 中的 ArrayBufferView)创建一个类似 C 的接口
- 创建一个高度可扩展的库,任何人都可以通过向对象 StringView.prototype 添加方法来扩展该库
- 为这种类似字符串的对象创建一组方法(从现在开始:stringViews),这些对象严格适用于数字数组,而不是创建新的不可变 JavaScript 字符串
- 使用除 JavaScript 的默认 UTF-16 DOMStrings 以外的 Unicode 编码
给予更多的灵活性。然而,当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
尽管使用 Blob/FileReader 的 Dennis 和 gengkev 解决方案有效,但我不建议采用这种方法。这是解决简单问题的异步方法,比直接解决方案慢得多。我在 html5rocks 中发布了一个更简单且(更快)的解决方案:http ://updates.html5rocks.com/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
解决方案是:
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;
}
编辑:
该编码API可以帮助解决这个字符串转换问题。查看Html5Rocks.com上Jeff Posnik对上述原始文章的回复。
摘抄:
编码 API 使原始字节和原生 JavaScript 字符串之间的转换变得简单,而不管您需要使用许多标准编码中的哪一种。
<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>
您可以使用TextEncoder
和TextDecoder
来自Encoding 标准,它由stringencoding 库填充,将字符串与 ArrayBuffers 相互转换:
var uint8array = new TextEncoder().encode(string);
var string = new TextDecoder(encoding).decode(uint8array);
Blob 比 String.fromCharCode(null,array);
但如果数组缓冲区变得太大,那将失败。我发现的最佳解决方案是使用String.fromCharCode(null,array);
并将其拆分为不会破坏堆栈但一次比单个字符快的操作。
大数组缓冲区的最佳解决方案是:
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());
}
和
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"
}
)
}
)