JavaScript 读写二进制数据
文章目录
预备知识
JS 操作二进制数据涉及到三个基本类型:
ArrayBuffer
、 TypedArray
和 DataView
为了达到最大的灵活性和效率,JavaScript 类型数组(Typed Arrays)将实现拆分为 缓冲 和 视图 两部分。ArrayBuffer 作用是作为缓冲区存放实际的二进制数据,TypedArray 和 DataView 作为视图访问底层的 ArrayBuffer。
ArrayBuffer 是一种数据类型,用来表示一个通用的、固定长度的二进制数据缓冲区;
TypedArray 含类型的Array View,例如
Uint8Array
;DataView 是一种底层接口,它提供有可以操作缓冲区中任意数据的读写接口。
TypedArray
下面用一个例子来说明 TypedArray 和 ArrayBuffer 的关系:
|
|
看两次输出的结果,分别是 [ 1, 2, 3, 4 ]
和 [ 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0 ]
这个 buffer 的16进制排列为:
|
|
采用16进制显示二进制数据是常用的做法,每一位数为 4bit,所以 1(int32) = 0x0001 。
在 Linux 系统中可使用命令
xxd output.bin
查看二进制。
Int32Array 为有符号32位整型(4 Bytes = 32 bit),Uint8Array 类型为无符号8位整型(1 Byte = 8 bit)。这里表示 TypedArray 中的每个数组成员所占的空间分别为 4 Bytes 和 1 Bytes,int32Arr[0]
即对应 uint8Arr[0:3]
即 Int32Array [ 1 ]
== Uint8Array [ 1, 0, 0, 0 ]
。这里为什么不是对应 Uint8Array [ 0, 0, 0, 1 ]
呢?这是因为这和字节序有关。
TypedArray 使用的是系统字节序 。现在 X86 CPU 使用的是小端字节序,即 最低有效位排列在最高有效位前面 ,例如 0x123456
使用小端字节序表示就是 563412
。
Tips
多个 view 可以绑定同一个 ArrayBuffer 来进行数据操作,就是类似于 C语言的 Union 的效果。
DataView
很多协议的每个字段是使用不同的字节长度的,因此单一的 TypedArray
在使用上很不方便。DataView
就提供了一个比 TypedArray 更加灵活的操作 ArrayBuffer 的接口。
目前 DataView 提供以下类型的读写方法,方法名称都是以 set/get 开头,例如: setInt8
/ getInt8
- Int8
- Uint8
- Int16
- Uint16
- Int32
- Uint32
- Float32
- Float64
读方法的函数签名为: dataview.getUint32(byteOffset [, littleEndian])
写方法的函数签名为: dataview.setUint32(byteOffset, value [, littleEndian])
需要注意的是 DataView 默认以大端字节序读写,在最后一个参数传 true
表示使用小端字节序操作。
由于 DataView 可以自由的选择字节序来读写 ArrayBuffer ,我们就可以通过 TypedArray (默认小端)和 DataView 两者的不同来判断出到底运行代码机器的字节序是大端还是小端。
|
|
Buffer
在这里顺便提一下 Node.JS 中的 Buffer 类型 。由于历史的原因,在 Node.JS 诞生之初,TypedArray 还没有被提出,所以 Node.JS 自己实现了个 Buffer 类型,随着 ECMA 2015 标准的提出,Buffer 已经转为使用 Uint8Array
实现。所以 Buffer
名字上给人感觉更像是 ArrayBuffer
,但是其实它是一个视图 (view)。
Buffer instances are also Uint8Array instances.
Reference
文章作者 sdvcrx
上次更新 2018-12-30