在字符串和ArrayBuffers之间转换


264

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


1
我对此没有任何经验,但是从API文档(khronos.org/registry/typedarray/specs/latest)来看,如果您构建了一个,Int8Array ArrayBufferView则可以简单地使用括号表示法来复制字符string[i] = buffer[i],反之亦然。
FK82

2
@ FK82,这似乎是一种合理的方法(Uint16Array对于JS的16位字符,请使用s),但是JavaScript字符串是不可变的,因此您不能直接将其分配给字符位置。我仍然需要复制String.fromCharCode(x)每个值在Uint16Array正常Array,然后调用.join()Array
kpozin 2011年

@kpozin:是的,真的没有这么想。
FK82 2011年

5
@kpozin事实证明,大多数现代JS引擎都对字符串连接进行了优化,以至于使用起来更便宜string += String.fromCharCode(buffer[i]);。似乎没有内置的方法可以在字符串和类型数组之间进行转换,这似乎很奇怪。他们必须知道会发生类似的事情。
下载

arrayBuffer.toString()对我来说很好。
公民conn 2015年

Answers:


129

2016更新 -五年来,规范中现在有了新方法(请参阅下面的支持),可以使用正确的编码在字符串和类型化数组之间进行转换。

TextEncoder

TextEncoder代表

TextEncoder接口表示为具体的方法的编码器,这是一个特定的字符编码,如utf-8iso-8859-2koi8cp1261gbk,... 编码器将代码点流作为输入,并发出字节流。

由于上述内容而编写的变更说明:(同上)

注意:Firefox,Chrome和Opera过去曾支持utf-8以外的其他编码类型(例如utf-16,iso-8859-2,koi8,cp1261和gbk)。自Firefox 48,Chrome 54和Opera 41起,为了符合规范,除utf-8以外没有其他可用的编码类型。

*)更新了规格(W3)和此处(whatwg)。

创建的实例后,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-8iso-8859-2koi8cp1261gbk,...的解码器需要一个字节流作为输入,并且发射的码点流。

此处可以找到所有可用的解码类型。

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

2
IE和Edge不支持TextDecoder:caniuse.com/#search=TextDecoder
Andrei Damian-Fekete


在2018-04-18上不支持Safari Mobile(ios):developer.mozilla.org/zh-CN/docs/Web/API/TextDecoder
古铜色的人

单线:var encoder = 'TextEncoder' in window ? new TextEncoder() : {encode: function(str){return Uint8Array.from(str, function(c){return c.codePointAt(0);});}};这样您就可以var array = encoder.encode('hello');
Yeti '18

1
问题TextEncoder是,如果字符串中有二进制数据(例如image),则您不想使用TextEncoder(显然)。代码点大于127的字符产生两个字节。为什么字符串中包含二进制数据?cy.fixture(NAME, 'binary')cypress)产生一个字符串。
x-yuri

175

尽管使用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对上述原始文章的回复。

摘抄:

无论您需要使用多种标准编码中的哪种,Encoding 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>

16
不幸的是,我对html5rocks的评论尚未获得批准。因此,这里有一个简短的答案。我仍然认为,这是不正确的方法,因为您会错过很多字符,尤其是由于当今大多数页面都采用UTF-8编码。一方面,对于更多特殊字符(比如亚洲),charCodeAt函数返回一个4字节的值,因此将它们切掉。另一方面,简单的英文字符将使ArrayBuffer增长两次(对于每个1字节字符,您将使用2字节)。想象一下,通过WebSocket发送英文文本,将需要两次时间(在实时环境中效果不佳)。
丹尼斯

9
三个示例:(1)This is a cool text!UTF8中为20字节-Unicode中为40字节。(2)ÄÖÜUTF8中的6个字节-Unicode中的6个字节。(3)☐☑☒UTF8中的9个字节-Unicode中的6个字节。如果要将字符串存储为UTF8文件(通过Blob和File Writer API),则不能使用这两种方法,因为ArrayBuffer将使用Unicode而不是UTF8。
丹尼斯2012年

3
我收到一个错误:未捕获RangeError:超出了最大调用堆栈大小。可能是什么问题呢?
2013年

6
@Dennis-JS字符串使用UCS2,而不是UTF8(甚至不使用UTF16),这意味着charCodeAt()始终返回值0->65535。任何需要4个字节结尾的UTF-8代码点都将以代理对表示(请参阅en.wikipedia .org / wiki /…)-即两个单独的16位UCS2值。
broofa 2013年

6
@jacob-我相信错误是因为可以传递给apply()方法的数组长度受到限制。例如String.fromCharCode.apply(null, new Uint16Array(new ArrayBuffer(246300))).length,我在Chrome中为我工作,但如果改用246301,我会收到RangeError异常
broofa 2013年

71

您可以使用stringencoding库中填充的Encoding标准TextEncoderEncoding标准来与ArrayBuffers之间来回转换字符串:TextDecoder

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

2
顺便说一下,默认情况下,这在Firefox中可用:developer.mozilla.org/en-US/docs/Web/API/TextDecoder.decode
Joel Richard

2
赞成比怪异的解决方法好得多的新API!
托马什Zato -恢复莫妮卡

1
这不适用于所有类型的字符。
大卫,

5
npm install text-encodingvar textEncoding = require('text-encoding'); var TextDecoder = textEncoding.TextDecoder;。不用了,谢谢。
Evan Hu

抱怨...如果我有一个现有的arraybuffer我想写一个字符串到我想我必须采取uint8array并第二次复制它?
shaunc

40

Blob比 String.fromCharCode(null,array);

但是如果数组缓冲区太大,那将失败。我发现最好的解决方案是使用String.fromCharCode(null,array);并将其拆分为不会破坏堆栈的操作,但一次要比单个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的大型字符串。


3
我们应该采用这种解决方案。因为这解决了比已接受的用例更多的用例
萨姆(Sam)2016年

24

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

在arrayBuffer2String()中,您是要调用callback(...)而不是console.log()吗?否则,回调参数将变为未使用状态。
Dan Phillimore

这看起来像是要走的路-感谢genkev和Dennis。似乎没有办法同步完成此操作很愚蠢,但是您可以做什么...
kpozin 2012年

JavaScript是单线程的。因此,FileReader是异步的,其原因有两个:(1)在加载(巨大)文件时(考虑到更复杂的应用程序),它不会阻止其他JavaScript的执行;(2)它不会阻止UI /浏览器(常见问题)与长时间执行的JS代码)。许多API是异步的。即使在XMLHttpRequest 2中,同步也被删除。
丹尼斯

我真的希望这对我有用,但是从字符串到ArrayBuffer的转换不能可靠地工作。我正在制作一个具有256个值的ArrayBuffer,并且可以将其转换为长度为256的字符串。但是然后,如果我尝试将其转换回ArrayBuffer-根据我最初的ArrayBuffer的内容,我得到了376个元素。如果您想重现我的问题,我会将ArrayBuffer视为Uint8Array中的16x16网格,并按a[y * w + x] = (x + y) / 2 * 16; 我尝试的方式计算出值getBlob("x"),并使用许多不同的mimetypes- 算不上运气。
Matt Cruikshank'5

18
在较新的浏览器中不推荐使用BlobBuilder。更改new BlobBuilder(); bb.append(buf);new Blob([buf]),将第二个函数中的ArrayBuffer通过new UintArray(buf)(或任何适用于基础数据类型的)强制转换为UintArray ,然后摆脱getBlob()调用。最后,为了保持清洁,将bb重命名为blob,因为它不再是BlobBuilder。
sowbug 2012年

18

以下所有关于从数组缓冲区获取二进制字符串的信息

我建议不要使用

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

因为它

  1. 在大缓冲区上崩溃(有人写了246300的“魔术”大小,但Maximum call stack size exceeded在120000字节缓冲区(Chrome 29)上出现错误)
  2. 它的性能确实很差(请参见下文)

如果您确实需要同步解决方案,请使用类似

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与String相互转换作者比较了我两种方法并获得完全相反的结果(他的测试代码在此处)为什么会有如此不同的结果?可能是因为他的测试字符串长1Kb(他称其为“ veryLongStr”)。我的缓冲区是一个非常大的JPEG图像,大小为2.4Mb。


13

更新,请参阅此答案的第二部分,在那儿,我(希望)提供了更完整的解决方案。)

我也遇到了这个问题,以下内容在FF 6中对我有用(一个方向):

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

不幸的是,当然,您最终得到的是数组中值的ASCII文本表示形式,而不是字符。但是,它(应该)比循环要有效得多。例如。对于上面的示例,结果为0004000000,而不是几个null字符和一个chr(4)。

编辑:

看后MDC 在这里,你可以创建一个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;

为了回答您的原始问题,这使您可以按以下方式转换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;
}

为了方便起见,这是一个function将原始Unicode转换String为的方法ArrayBuffer(仅适用于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-> StringArrayBuffer再次返回,其中字符串可以存储在例如。.localStorage:)

希望这可以帮助,


1
我认为这不是一种有效的方法(就时间或空间而言),这是一种非常不寻常的存储二进制数据的方法。
kpozin 2012年

@kpozin:据我所知,没有其他方法可以将二进制数据存储在localStorage中
Dan Phillimore 2012年

1
使用base64编码怎么办?
Nick Sotiros,2015年

13

与此处的解决方案不同,我需要与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кирилицаαβγδεζηあいうえお"

8

如果字符串中包含二进制数据(从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、80、152、118、92、144、48

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

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


7

我发现我在使用这种方法时遇到了问题,基本上是因为我试图将输出写入文件中并且未正确编码。由于JS似乎使用UCS-2编码(sourcesource),因此我们需要进一步扩展此解决方案,这是我使用的增强型解决方案。

我对普通文本没有任何问题,但是当使用阿拉伯语或韩语时,输出文件没有所有字符,而是显示错误字符

文件输出: ","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}

我从dennis的解决方案中获取了信息,并且发现了这篇文章

这是我的代码:

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组件中编码字符串,然后将其转换为相应的8字节字符。这样,我们不会丢失超过1个字节长的UTF8字符所提供的信息。


6

如果您使用了巨大的数组示例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;
}

4

嗯,这是做同样事情的一种令人费解的方法:

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

3

与mangini的从转换解决方案打后ArrayBufferString- 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变量设置为其他值来更改它。重要的是要有偶数。

关于性能的注意事项-我没有对该解决方案进行任何性能测试。但是,由于它基于先前的解决方案,并且可以处理大型数组,因此我认为没有理由不使用它。


您可以使用typedarray.subarray在指定的位置和大小处获取块,这就是我在js中读取二进制格式的标头的方法
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;
  }

如果字符串包含unicode字符,则此代码有错误。示例:arrayBufferToString(stringToArrayBuffer('🐴'))==='44'
xmcp

2

对于node.js以及使用https://github.com/feross/buffer的浏览器

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

注意:这里的解决方案不适用于我。我需要支持node.js和浏览器,只是将UInt8Array序列化为字符串。我可以将其序列化为一个数字[],但是会占用不必要的空间。使用该解决方案,因为它是base64,所以我不必担心编码。万一其他人遇到同样的问题...我的两分钱



1

atob()返回的“本地”二进制字符串是每个字符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;
}

1

是:

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

0

我建议不要使用不赞成使用的API,例如BlobBuilder

Blob对象早已不推荐使用BlobBuilder。将丹尼斯答案(使用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);

}

请注意,与不推荐使用的方法相比,此方法更清洁,更不膨胀。是的,这绝对是这里要考虑的问题。


我的意思是,是的,但是Blob构造函数在2012年还没有真正可用;)
gengkev 2014年


0

我用这个为我工作。

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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.