计算机就是处理 0 和 1,很尴尬的是在引入 TypedArray 之前,JavaScript 没有操作二进制数据流的机制,Buffer 类用一种更适合 Node.js 的方式实现了 Uint8Array API,用于在 TCP 流、文件系统操作等场景处理二进制字节
bit 与 Byte
- bit 是我们常说的比特,比特币就是以此命名的,bit 是二进制的最小信息单位,1 bit 就是我们说的 1 位,64 位操作系统 CPU 一次能处理
位的数据
- Byte 被翻译为字节,是计量存储或者传输流量的单位,硬盘容量、网速等说的都是字节,一个英文字符是一个字节,也就是我们说的 1B,中文字符通常是两个字节(Node.js 中使用三个字节)
Node.js 的 Buffer 的单位是 1 byte,意味着存储着 8 个 bit,也就是1 byte = 8 bit
位的数据
Buffer 的特性
Buffer 类的实例类似于 0 到 255 之间的整型数组(其他整数会通过 & 255 操作强制转换到此范围),Buffer 是一个 JavaScript 和 C++ 结合的模块,对象内存不经 V8 分配,而是由 C++ 申请、JavaScript 分配。缓冲区的大小在创建时确定,不能调整。
Buffer 对象实在过于常用,被直接内置到全局变量中,使用时候无需 require 引入
实例化 Buffer
在 Node.js v6 之前都是通过调用构造函数的方式实例化 Buffer,根据参数返回不同结果。
处于安全性原因,这种方式在 v6 后的版本中已经被废除,现在提供了三个职责清晰的函数处理实例化 Buffer 的工作
Buffer.from
Buffer.from 支持四种参数类型
Buffer.from(string [, encoding])
:返回一个包含给定字符串的 Buffer,默认编码utf-8
Buffer.from(buffer)
:返回给定 Buffer 的一个副本 BufferBuffer.from(array)
:返回一个内容包含所提供的字节副本的 Buffer,数组中每一项是一个表示八位字节的数字,所以值必须在 0 ~ 255 之间,否则会取模Buffer.from(arrayBuffer)
:返回一个与给定的 ArrayBuffer 共享内存的新 BufferBuffer.from(object[, offsetOrEncoding[, length]])
:取 object 的 valueOf 或 Symbol.toPrimitive 初始化 Buffer ```javascript // 字符串 const str = ‘Hello’ const buf = Buffer.from(str) //
// buffer
Buffer.from(buf) //
// 数组
const arr = [0, -1, 1, 255, 256] // 超过 255 会取模
Buffer.from(arr) //
// ArrayBuffer
const arrayBuffer = new Uint8Array(4)
// arrayBuffer = [ 0, 255, 255, 0 ]
arrayBuffer[0] = 0
arrayBuffer[1] = -1
arrayBuffer[2] = 255
arrayBuffer[3] = 256
// 共享内存
const buf2 = Buffer.from(arrayBuffer.buffer) //
// 对象
Buffer.from(new String(‘this is a test’)) //
<a name="A8RGp"></a>
## Buffer.alloc
`Buffer.alloc(size, [, fill, [, encoding]])`
- 分配一个大小为 size 字节的新 Buffer,如果 fill 为 undefined,则用 0 填充 Buffer
- size <integer> 新 Buffer 的所需长度
- fill <string> | <Buffer> | <Uint8Array> | <integer> 用于预填充新 Buffer 的值。默认值: 0
- encoding <string> 如果 fill 是一个字符串,则这是它的字符编码。默认值: `utf8`
```javascript
const buf = Buffer.alloc(5);
console.log(buf); // <Buffer 00 00 00 00 00>
如果指定了 fill,则分配的 Buffer 通过调用 buf.fill(fill)
进行初始化。
const buf = Buffer.alloc(5, 'a');
console.log(buf); // <Buffer 61 61 61 61 61>
Buffer.allocUnsafe
Buffer.allocUnsafe(size)
:分配一个大小为 size 字节的新 Buffer,allocUnsafe 执行速度比 alloc 快,但方法的名字听起来很不安全,确实也不安全
const buf = Buffer.allocUnsafe(10);
console.log(buf); // 打印内容不确定
Buffer 模块会预分配一个内部的大小为 Buffer.poolSize 的 Buffer 实例,作为快速分配的内存池(Buffer 池),可以用于使用 Buffer.allocUnsafe()
创建新的 Buffer 实例
Buffer.alloc()
永远不会使用内部的 Buffer 池。Buffer.allocUnsafe(size)
在 size <= Buffer.poolSize / 2
时将会使用内部的 Buffer 池
:::warning
当调用 Buffer.allocUnsafe()
时分配的内存段尚未初始化(不归零),这样分配内存速度很块,但分配到的内存片段可能包含旧数据。如果在使用的时候不覆盖这些旧数据就可能造成内存泄露,虽然速度快,尽量避免使用
:::
Buffer.allocUnsafeSlow
创建一个大小为 size 字节的新 Buffer,直接通过 c++ 进行内存分配,不会进行旧值填充,当分配的空间小于 4KB的时候,allocUnsafe 会直接使用预分配的 Buffer,因此速度比 allocUnsafeSlow 要快,当大于等于4KB的时候二者速度相差无异
编码
Buffer 目前支持以下几种编码格式
- ascii
- utf8
- utf16le
- base64
- binary
- hex
Buffer 和 String 转换
Buffer.from(string [, encoding])
字符串转为 Bufferbuffer.toString([encoding[, start[, end]]])
Buffer 实例的 toString 方法可以将 Buffer 转为字符串
const buf = Buffer.from('test', 'utf-8') // <Buffer 74 65 73 74>
console.log(buf.toString()); // test
console.log(buf.toString('hex')); // 74657374
Buffer 拼接
Buffer.concat(list[, totalLength])
方法可以把多个 Buffer 实例拼接为一个 Buffer 实例
const buf1 = Buffer.from('a');
const buf2 = Buffer.from('b');
const buf3 = Buffer.from('c');
const buf = Buffer.concat([buf1, buf2, buf3]);
console.log(buf); // <Buffer 61 62 63>
StringDecoder
乱码
在 NodeJS 中一个汉字由三个字节表示,如果我们处理中文字符的时候使用了不是 3 的倍数的字节数就会造成字符拼接乱码问题
const buf = Buffer.from('中文字符串!');
let gap = 5
for (let i = 0; i < buf.length; i += gap) {
var b = Buffer.allocUnsafe(gap);
buf.copy(b, 0, i);
console.log(b.toString());
}
解决方式
使用 string_decoder 模块可以解决这个问题
const { StringDecoder } = require('string_decoder')
const decoder = new StringDecoder('utf8')
const buf = Buffer.from('中文字符串!')
let gap = 5
for (let i = 0; i < buf.length; i += gap) {
var b = Buffer.allocUnsafe(gap);
buf.copy(b, 0, i);
console.log(decoder.write(b));
}
:::info StringDecoder 在得到编码后,知道宽字节在 utf-8 下占 3 个字节,所以在处理末尾不全的字节时,会保留到第二次 write()。目前只能处理 UTF-8、Base64 和 UCS-2/UTF-16LE。 :::
Buffer 的其他常用 API
还有一些 Buffer 常用的 API,比较简单不用代码示例了
- Buffer.isBuffer:判断对象是否为 Buffer
- Buffer.isEncoding:判断 Buffer 对象编码
- buf.length:返回 内存为此 Buffer 实例所申请的字节数,并不是 Buffer 实例内容的字节数
- buf.indexOf:和数组的 indexOf 类似,返回某字符串、acsii 码或者 buf 在改 buf 中的位置
- buf.copy:将一个 buf 的(部分)内容复制到另外一个 buf 中
了解了这些内容就可以进入 stream 的学习了