在字符串和ArrayBuffer之间进行转换

466

是否有一种通常被接受的技术,能够有效地将JavaScript字符串转换为ArrayBuffers,反之亦然?具体而言,我希望能够将一个ArrayBuffer的内容写入localStorage中,并随后读取。


1
我在这方面没有任何经验,但根据API文档(http://www.khronos.org/registry/typedarray/specs/latest/)的判断,如果您构建一个`Int8Array` ArrayBufferView,可能可以简单地使用括号表示法来复制字符string[i] = buffer[i],反之亦然。 - FK82
2
@FK82,这看起来是一个合理的方法(使用Uint16Array来处理JS的16位字符),但JavaScript字符串是不可变的,所以您不能直接赋值给一个字符位置。我仍然需要将Uint16Array中每个值的String.fromCharCode(x)复制到普通的Array中,然后在Array上调用.join() - kpozin
7
原文:@kpozin 发现大多数现代 JS 引擎已经优化了字符串拼接,以至于仅使用string += String.fromCharCode(buffer[i]);更加高效。没有内置方法在字符串和类型数组之间转换似乎很奇怪。他们本应该知道会出现这样的情况。翻译:发现大多数现代JavaScript引擎已经将字符串拼接优化到一个程度,在使用“string += String.fromCharCode(buffer[i])”时比其他方式更为便宜。没有内置的方法可以在字符串和类型数组之间进行转换,这似乎有点奇怪,因为他们应该知道这种情况会出现。 - Erin
1
arrayBuffer.toString() 对我来说运行良好。 - citizen conn
1
@citizen conn - 我不知道你使用的浏览器是什么,但在 Chrome 上,arrayBuffer.toString() 只会返回 "[object ArrayBuffer]"。并不是很有帮助。 - mrec
显示剩余4条评论
29个回答

11

对于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序列化为字符串。我可以将其序列化为number[],但那会占用不必要的空间。使用这个解决方案,我不需要担心编码问题,因为它是base64编码。以防其他人遇到同样的问题...我的建议。

7
我发现这种方法存在问题,因为我试图将输出写入文件,但它没有正确的编码方式。由于JS似乎使用UCS-2编码(来源1来源2),我们需要进一步完善此解决方案,这里是我的增强版解决方案,对我有效。

我对普通文本没有问题,但对于阿拉伯语或韩语等其他语言时,输出文件并不包含所有字符,而是显示错误字符。

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

原始内容: ","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;
 }

这使我能够将内容保存到文件中,而不会出现编码问题。
工作原理:基本上它取出由单个8字节块组成的UTF-8字符,并将它们保存为单个字符(因此,这种方式构建的UTF-8字符可能由1-4个这样的字符组成)。 UTF-8以长度从1到4字节的格式对字符进行编码。我们在这里做的是将字符串编码为URI组件,然后将该组件翻译为相应的8字节字符。通过这种方式,我们不会丢失UTF8字符提供的多于1个字节长的信息。

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

5

好的,这里有一种有点复杂的方法可以实现同样的功能:

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

5

最近我也需要在我的一个项目中这样做,所以进行了深入研究,并从Google的开发者社区得到了一个简单明了的解释:

将ArrayBuffer转换为字符串

function ab2str(buf) {
  return String.fromCharCode.apply(null, new Uint16Array(buf));
}
// Here Uint16 can be different like Uinit8/Uint32 depending upon your buffer value type.

字符串转ArrayBuffer

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;
}
//Same here also for the Uint16Array.

如需更详细的参考,请参阅 Google 的此博客


这个方法在我的文本中插入了“NUL”字符... 对我有效的解决方案是https://dev59.com/fWw05IYBdhLWcg3w41wp#45722000 - AymKdn

5
以下是一个可用的Typescript实现:
bufferToString(buffer: ArrayBuffer): string {
    return String.fromCharCode.apply(null, Array.from(new Uint16Array(buffer)));
}

stringToBuffer(value: string): ArrayBuffer {
    let buffer = new ArrayBuffer(value.length * 2); // 2 bytes per char
    let view = new Uint16Array(buffer);
    for (let i = 0, length = value.length; i < length; i++) {
        view[i] = value.charCodeAt(i);
    }
    return buffer;
}

在使用crypto.subtle进行大量操作时,我已经使用过这个。


这是我最喜欢的方法,但是,如果“data”是缓冲区,你该如何进行检查? - Alan Daniel
如果数据是字符串。如果它是UTF16或更小的编码,可以使用此函数将其转换为ArrayBuffer。这并不意味着缓冲区对于其他事情(例如转换为图像)有用。如果您想知道这一点,您需要查看图像格式并进行嗅探。大多数文件类型在开头都有元数据。 - N-ate

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

2
如果字符串包含Unicode字符,则此代码存在错误。例如:arrayBufferToString(stringToArrayBuffer(''))==='44' - xmcp

4

假设您拥有一个arrayBuffer二进制字符串:

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

然后您将文本分配给状态。


3

是的:

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

3

我用过这个,对我很有效。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接