前端字节操作方法
ArrayBuffer
- 前端有一个类叫ArrayBuffer,它是一个数组,可以用来存储一定数量的“字节”。
- 一个字节等于8位,一位(1bit)可以存一个二进制数据,要么存0,要么存1。
- 那么一个字节就可以存8个二进制数字,也就是说一个字节所能存的数字一定在 00000000~11111111 范围内,超出这个范围就得用更多字节来存。数字还分有符号和无符号,这里不展开了。
一个字节可以存储的数据量
Typed Array 和 Data View
- ArrayBuffer不可以直接读写,得借住
Typed Array和Data View - 比如借助
Typed Array
// 创建一个长度为10的buffer
const buffer = new ArrayBuffer(10)
// 创建一个无符号8位Typed Array
const view = new Uint8Array(buffer)
view[0] = 255
view[1] = 100
- 又比如借助
Data View
const data = new ArrayBuffer(10)
const view = new DataView(data)
view.setInt8(0, 255)
字节序
如上所说,当调用DataView.setInt8(0, 255)时,内存中的数据如下:
查看MDN文档可以看到DataView.setInt8是没有第三个参数的,而DataView.setInt16 / DataView.setInt32等需要传第三参数
littleEndian,这就是字节序。因为setInt8正好是设置一个字节,没有顺序的概念;从setInt16开始,就要一次向多个字节中写数据,就有写入顺序的概念。
先看一下大端字节序的表现
const data = new ArrayBuffer(10)
const view = new DataView(data)
view.setInt32(0, 258, false) // 第三个参数传false,也是默认值
写入32位整形需要一次操作4个字节(4*8=32)
再看一下小端字节序的表现
const data = new ArrayBuffer(10)
const view = new DataView(data)
view.setInt32(0, 258, true) // 第三个参数传true,表示小端字节序
读数据时的字节序
读字节与写字节一样,如果你读一个字节,那没有字节序可言,但你读多个字节时必须要指定字节序。比如
const data = new ArrayBuffer(10)
const view = new DataView(data)
view.setInt32(0, 258, true)
view.getInt8(0) // 只读一个字节
view.getInt32(0, true) // 读一个32位整形(4字节),第二个参数指定使用什么字节序
一般以什么字节序写入,就用什么字节序读出,否则会读到与写入时不一样的结果。
比如下面这张图,我是用大端字节序写入了一个258数字。
正确的计算方式是
如果以小端字节序写入,内存里就是下面这样。
如果却用大端字节序来读就变成了
那你会发现,写入258,读出却是33619968
用字节表达现实世界的信息
其实字节是纯粹的,每个字节里就存了8位,每位有两种可能,0或1。至于一次读几个字节;以什么样的顺序去读;读出来的这些字节表示什么意义,那是需要人去设计的。
比如
-
一个字节可以存0-255,RGB颜色值正好是0-255,那3个字节就可以表示一组rgb,也就是一个像素信息,一个这样的字节数组就可以表示一张图片。
-
在ASCII码中一个字节可以表示一个字符,字母A的ASCII码是65,二进制表示就是 01000001。也就是说,当你读到一字节里面的数据是 01000001 ,又和对方约定了,这里表示的是一个通过ASCII编码的信息,那你就知道了,对方在这个字节里放了一个字母A。
-
gbk编码用固定两个字节表示一个符号,如果你读到两个字节 1100110111110101 (16进制表示是 CDF5),通过gbk对照表就知道这表示 “王” 这个汉字。