在字符串和ArrayBuffers之间转换
有没有一个被普遍接受的技术,有效地将JavaScript字符串转换为ArrayBuffers ,反之亦然? 具体来说,我想能够将一个ArrayBuffer的内容写入localStorage
并将其读回。
更新2016年 – 现在有五年的新内置方法可用于使用适当的编码在字符串和类型数组之间进行转换。
TextEncoder
TextEncoder
表示 :
TextEncoder
接口表示一个特定方法的编码器,它是一个特定的字符编码,如utf-8
,iso-8859-2
,koi8
,cp1261
,gbk
,…编码器将一串代码点作为输入并发出一个字节流。
在创建TextEncoder
的实例之后,它将采用一个字符串并使用给定的编码参数对其进行编码:
if (!("TextEncoder" in window)) alert("Sorry, this browser does not support TextEncoder..."); var enc = new TextEncoder("utf-8"); console.log(enc.encode("This is a string converted to a Uint8Array"));
尽管使用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>
您可以使用由stringencoding库 TextDecoder
的编码标准中的 TextEncoder
和TextDecoder
将字符串转换为ArrayBuffers或从ArrayBuffers转换字符串:
var uint8array = new TextEncoder(encoding).encode(string); var string = new TextDecoder(encoding).decode(uint8array);
Blob比String.fromCharCode(null,array);
慢得多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" } ) } )
( 更新请看这个答案的后半部分,我有(希望)提供了一个更完整的解决方案。)
我也碰到过这个问题,FF 6中的以下作品(单向):
var buf = new ArrayBuffer( 10 ); var view = new Uint8Array( buf ); view[ 3 ] = 4; alert(Array.prototype.slice.call(view).join(""));
不幸的是,当然,最后是数组中值的ASCII文本表示,而不是字符。 但它仍然(应该)比循环更有效率。 例如。 对于上面的例子,结果是0004000000
,而不是几个空字符和一个字符(4)。
编辑:
在这里查看MDC之后,可以按如下所示从Array
创建一个ArrayBuffer
:
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;
要回答你原来的问题,这允许你转换ArrayBuffer
< – > String
,如下所示:
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; }
为了方便起见,下面是将原始Unicode String
转换为ArrayBuffer
的function
(仅适用于ASCII /单字节字符)
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 ]);
以上允许你从ArrayBuffer
– > String
&返回到ArrayBuffer
再次,其中的字符串可能存储在例如。 .localStorage
🙂
希望这可以帮助,
担
以下全部是关于从数组缓冲区获取二进制字符串的
我建议不要使用
var binaryString = String.fromCharCode.apply(null, new Uint8Array(arrayBuffer));
因为它
- 在大缓冲区崩溃 (有人写了关于“魔术”大小的246300,但我得到
Maximum call stack size exceeded
错误120000字节缓冲区(铬29)) - 它的表现确实很差 (见下文)
如果你确实需要同步解决方案使用类似
var binaryString = '', bytes = new Uint8Array(arrayBuffer), length = bytes.length; for (var i = 0; i < length; i++) { binaryString += String.fromCharCode(bytes[i]); }
它和前一个一样慢,但工作正常。 看来在写这个的时候,这个问题没有很快的同步解决方案(本主题中提到的所有库对于它们的同步特性使用相同的方法)。
但是我真正推荐的是使用Blob
+ FileReader
方法
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' })); }
唯一的缺点(不是全部)是异步的 。 而且比以前的解决方案快8-10倍 ! (一些细节:在我的环境中,同步解决方案对于2.4Mb缓冲区花费了950-1050毫秒,但是对于相同数量的数据,使用FileReader的解决方案花费了大约100-120毫秒的时间,并且我已经在100Kb缓冲区上测试了两个同步解决方案,几乎在同一时间,所以使用'apply'循环并不会太慢)。
顺便说一句: 如何将ArrayBuffer转换为字符串作者和比较像我这样的两种方法,并得到完全相反的结果( 他的测试代码在这里 )为什么如此不同的结果? 可能是因为他的测试字符串是1Kb(他称之为“veryLongStr”)。 我的缓冲区是一个非常大的2.4 MB大小的JPEG图像。
不像这里的解决方案,我需要转换为/从UTF-8数据。 为此,我使用(un)escape /(en)decodeURIComponent技巧编码了以下两个函数。 他们非常浪费内存,分配的编码utf8字符串的长度的9倍,虽然这些应该由gc恢复。 只是不要使用它们为100MB的文本。
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))); }
检查它的工作原理:
strFromUtf8Ab(utf8AbFromStr('latinкирилицаαβγδεζηあいうえお')) -> "latinкирилицаαβγδεζηあいうえお"
我发现我有这种方法的问题,基本上是因为我试图写输出到一个文件,并没有正确编码。 由于JS似乎使用UCS-2编码( 源 , 源 ),我们需要进一步扩展这个解决方案,这是我的增强型解决方案。
我对通用文本没有任何困难,但是当它归结为阿拉伯文或韩文时,输出文件没有全部字符,而是显示错误字符
文件输出: ","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}
原文: ","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}
我从丹尼斯解决方案的信息,我发现这个职位 。
这是我的代码:
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; }
这使我可以将内容保存到一个文件,而不会出现编码问题。
它是如何工作的:它基本上把构成一个UTF-8字符的单个8字节块保存为单个字符(因此,这样构建的UTF-8字符可以由这些字符中的1-4个字符组成)。 UTF-8编码字符的长度为1到4个字节。 我们在这里做的是在URI组件中编码sting,然后把这个组件翻译成相应的8字节字符。 这样我们就不会丢失长度超过1个字节的UTF8字符给出的信息。
好吧,做同样的事情有点复杂
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());
编辑: BlobBuilder长期以来一直赞成Blob构造函数,这在我第一次写这篇文章时并不存在。 这是一个更新的版本。 (是的,这一直是一个非常愚蠢的方式来做转换,但它只是为了好玩!)
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]));
看到这里: https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays/StringView (基于JavaScript的ArrayBuffer接口字符串类似C接口)
玩了mangini的解决方案,从ArrayBuffer
转换为String
– ab2str
(这是我发现的最优雅和最有用的 – 谢谢!),我处理大型数组时遇到了一些问题。 更具体地说,调用String.fromCharCode.apply(null, new Uint16Array(buf));
抛出一个错误:
arguments array passed to Function.prototype.apply is too large
。
为了解决这个问题(旁路),我决定分块处理输入的ArrayBuffer
。 所以修改后的解决方案是:
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; }
块大小设置为2^16
因为这是我发现在我的开发环境中工作的大小。 设置较高的值会导致同样的错误再次出现。 可以通过将CHUNK_SIZE
变量设置为不同的值来更改它。 有一个偶数是重要的。
关于性能的说明 – 我没有为这个解决方案做任何性能测试。 但是,由于它是基于以前的解决方案,并且可以处理大型数组,我没有理由不使用它。
欢迎任何评论( – :
如果你使用巨大的数组例子arr.length=1000000
你可以通过这段代码来避免堆栈回调的问题
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 }
从顶部反转功能mangini答案
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; }
atob()返回的“native”二进制字符串是每个字符1个字节的数组。
所以我们不应该在一个字符中存储2个字节。
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; }
我建议不要使用BlobBuilder等已弃用的API
BlobBuilder早已被Blob对象所弃用。 比较Dennis的答案 – 其中使用BlobBuilder的代码 – 与下面的代码:
function arrayBufferGen(str, cb) { var b = new Blob([str]); var f = new FileReader(); f.onload = function(e) { cb(e.target.result); } f.readAsArrayBuffer(b); }
请注意,与已弃用的方法相比,多少更干净和更少的臃肿…是的,这绝对是在这里考虑的事情。
var decoder = new TextDecoder (); var string = decoder.decode (arrayBuffer);
请参阅https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/decode
我用这个,为我工作。
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; }
ES2015:
a=Uint8Array.from(s,(x)=>x.charCodeAt(0))
Uint8阵列(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)
“RRFl¶é(÷LÎÎW0Náò8ìÉPPv\ 0”