JS如何从ArrayBuffer中解码字符串

最近遇到一个问题,拿到一个ArrayBuffer,知道它是以gb2312编码的文档,那么如何使用javascript从中解码出字符串。下面介绍常用的解决方案。

基础知识

字符编码

字符编码(英语:Character encoding)、字集码是把字符集中的字符编码为指定集合中某一对象(例如:比特模式、自然数序列、8位组或者电脉冲),以便文本在计算机中存储和通过通信网络的传递。常见的例子包括将拉丁字母表编码成摩斯电码和ASCII。其中,ASCII将字母、数字和其它符号编号,并用7比特的二进制来表示这个整数。通常会额外使用一个扩充的比特,以便于以1个字节的方式存储。

因此,如果不知道字符存储的编码方案,那么只能得到一堆无意义的数字,无法从中解码出正确的字符信息。

ArrayBuffer

ArrayBuffer对象、TypedArray对象、DataView对象是JavaScript操作二进制数据的一个接口。这些对象早就存在,属于独立的规格,ES6将它们纳入了ECMAScript规格,并且增加了新的方法。

这些对象原始的设计目的,与WebGL项目有关。所谓WebGL,就是指浏览器与显卡之间的通信接口,为了满足JavaScript与显卡之间大量的、实时的数据交换,它们之间的数据通信必须是二进制的,而不能是传统的文本格式。文本格式传递一个32位整数,两端的JavaScript脚本与显卡都要进行格式转化,将非常耗时。这时要是存在一种机制,可以像C语言那样,直接操作字节,将4个字节的32位整数,以二进制形式原封不动地送入显卡,脚本的性能就会大幅提升。

二进制数组就是在这种背景下诞生的。它很像C语言的数组,允许开发者以数组下标的形式,直接操作内存,大大增强了JavaScript处理二进制数据的能力,使得开发者有可能通过JavaScript与操作系统的原生接口进行二进制通信。

二进制数组由三个对象组成。

  • ArrayBuffer对象:代表内存之中的一段二进制数据,可以通过“视图”进行操作。“视图”部署了数组接口,这意味着,可以用数组的方法操作内存。

  • TypedArray对象:用来生成内存的视图,通过9个构造函数,可以生成9种数据格式的视图,比如Uint8Array(无符号8位整数)数组视图, Int16Array(16位整数)数组视图, Float32Array(32位浮点数)数组视图等等。

  • DataView对象:用来生成内存的视图,可以自定义格式和字节序,比如第一个字节是Uint8(无符号8位整数)、第二个字节是Int16(16位整数)、第三个字节是Float32(32位浮点数)等等。

简单说,ArrayBuffer对象代表原始的二进制数据,TypedArray对象代表确定类型的二进制数据,DataView对象代表不确定类型的二进制数据。它们支持的数据类型一共有9种(DataView对象支持除Uint8C以外的其他8种)。

解决方案

UTF-16的编码解码

下面的解决方案只能解码UTF-16编码的字符串,而且当ArrayBuffer的长度过大时,会报“ Maximum call stack size exceeded”的错误。

1
2
3
4
5
6
7
8
9
10
11
12
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;
}

gb2312解码

下面的解决方案能解码指定编码的字符串,包括utf-8,utf-16, iso-8859-2, koi8, cp1261, and gbk等。

1
2
3
4
5
function ab2str(arrayBuf, encodeType) {
var decoder = new TextDecoder(encodeType)
var u8arr = new Uint8Array(arrayBuf)
return decoder.decode(u8arr)
}

参考链接

  1. How to convert ArrayBuffer to and from String,by Renato Mangini.
  2. 字符编码,by wikipedia.
  3. Converting arraybuffer to string : Maximum call stack size exceeded,by stackoverflow.
  4. “RangeError: Maximum call stack size exceeded” Why?,by stackoverflow.
  5. TextDecoder,by mozilla.
  6. String.fromCharCode(),by mozilla.
  7. 二进制数组,by 阮一峰.
  8. ArrayBuffer,by 阮一峰.