前言
计算机底层存储的数据都是二进制的。在前端开发中一般都是实现页面效果,很少会直接操作二进制的数据。而在 Node.js 擅长的领域,比如在服务端程序开发中,经常会读取文件,处理文件,和二进制数据打交道比较多。
Buffer
对象是 Node.js 提供的一个全局对象,专门用来处理二进制数据。
二进制数据在开发中,操作和表示都非常不方便,所以 Buffer 对象采用十六进制来表示二进制数据。比如二进制数据 00001111
,转为十六进制就是 f
。用 buffer 表示就是:
let buffer = Buffer.from([0b1111])
console.log(buffer) // <Buffer 0f>
创建 buffer
Buffer 既然是一个对象,那么一般可以通过 new 的方式创建它的实例:
let buffer = new Buffer()
但是这种方式由于安全性已经废弃了。在实例化时会报错。
文档推荐使用这几种新的方法来创建:
- Buffer.alloc:创建指定字节大小的 buffer。
- Buffer.allocUnsafe:从名字看就知道是一个不安全的 API 。不推荐使用。
- Buffer.from:使用传入的数据创建 buffer,最常用的方法。
Buffer.alloc
创建 buffer 的时候需要指定 buffer 的大小。
// 创建一个 3 字节大小的buffer
let buffer = Buffer.alloc(3)
console.log(buffer)
// buffer 使用十六进制来描述二进制数据,长这样:
// 由于没有往里存东西,所以都为 00
// <Buffer 00 00 00>
Buffer.allocUnsafe
let buffer = Buffer.allocUnsafe(3)
console.log(buffer) // 值不确定
为什么值不确定呢?
由于 V8 引擎的 GC 机制,会产生内存碎片,一块空间不再使用了,但是还没有被回收。此时就有可能会被这个方法拿来创建一个 buffer ,所以创建出来的 buffer 可能并不是空的。这就是为什么值不确定的原因。同样也是不推荐使用该方法的原因。
其实该方法也谈不上不安全,只是不干净而已,并不影响最终的使用,因为创建之后总会用新的数据覆盖它。
Buffer.from
该方法接收两个参数,第一个参数是用来创建 buffer 的数据,也就是要存到缓冲区的数据,有三种类型:字符串,数组和 Buffer 实例。
第二个参数用来指定编码格式,默认为 utf-8
。
使用字符串创建 buffer
let buffer1 = Buffer.from('abc')
console.log(buffer1)
// 默认使用 utf-8 编码,一个字符占一个字节,所以有三个字节大小
// <Buffer 61 62 63>
let buffer2 = Buffer.from('昆吾')
console.log(buffer2)
// 默认使用 utf-8 编码,一个汉字占3个字节。所以所以生成的 buffer有6个字节
// <Buffer e6 98 86 e5 90 be>
使用数组创建 buffer
数组里可以放二进制,十进制,八进制,十六进制,字符串。
let buffer1 = Buffer.from([1, 2, 3])
let buffer2 = Buffer.from([0x4, 0x5, 0x6])
let buffer3 = Buffer.from([0b111, 0b1000, 0b1001])
console.log(buffer1)
console.log(buffer2)
console.log(buffer3)
/*
<Buffer 01 02 03>
<Buffer 04 05 06>
<Buffer 07 08 09>
*/
使用 buffer 创建 buffer
let buffer1 = Buffer.alloc(3)
let buffer2 = Buffer.from(buffer1)
console.log(buffer1)// <Buffer 00 00 00>
console.log(buffer2)// <Buffer 00 00 00>
创建出来的两个 buffer 长的一样,但是两块独立的内存空间,互不影响。
Buffer 的实例方法
观察上文中创建的 buffer,比如 <Buffer 01 02 03>
,非常像是一个数组。
我们可以将 buffer 看作是一个存放二进制数据的数组,只不过每一项都是一字节大小的数据,而非我们熟悉的数字、字符串等。
Buffer 是一个全局对象,打印一下它的原型对象:
console.log(Buffer.prototype)
观察可知,它继承自 Uint8Array
对象,并且有非常多的原型方法。我们主要学习一些常用的,包括 copy
、toString
、indexOf
、includes
、fill
、write
、subarray
等。
toString
将从 buffer 中读取出来的数据转为字符串类型。
接收三个可选参数,分别是编码格式,从 buffer 中读取数据的起始位置。
let buffer = Buffer.from('昆吾kw')
console.log(buffer) // <Buffer e6 98 86 e5 90 be 6b 77>
console.log(buffer.toString()) // 昆吾kw 读取全部数据转为字符串
console.log(buffer.toString('utf8', 6)) // kw 从第7个字节开始读取
console.log(buffer.toString('utf8', 0, 6)) // 昆吾 读取第1个到第6个字节
我们发现,buffer 在使用时和数组非常像,也可以通过从0开始的索引来指定位置,0索引表示的就是第一个字节。后面的示例中我们以此表示。
copy
把 buffer 中的数据拷贝到另一个 buffer 中。
接收四个参数,第一个为拷贝数据的目标 buffer,必填。第二个参数是目标 buffer 从哪个位置开始接收接收数据。后两个参数指定从源 buffer 中取数据的起始位置。
let target = Buffer.alloc(8)
let source = Buffer.from('昆吾kw')
// 部分拷贝。将 source 中 0-5 个字节也就是“昆吾” 拷贝至 target 中,target 从 0 位置开始接收
source.copy(target, 2, 0, 6)
console.log(target) // <Buffer 00 00 e6 98 86 e5 90 be>
console.log(target.toString()) // 昆吾
// 完整拷贝。将 source 中的数据拷贝到 target 中。
source.copy(target)
console.log(target.toString()) // 昆吾kw
indexOf
类似字符串和数组中的 indexOf 方法,用于查找目标中匹配到的第一个元素的位置。
在 buffer 中,就是找匹配到的字节的位置。
接收两个参数。第一个是要查找的内容,第二个指定从 buffer 的哪个字节开始查找。找到了则返回字节的位置,找不到返回 -1。
let buffer = Buffer.from('hello, world')
console.log(buffer) // <Buffer 68 65 6c 6c 6f 2c 20 77 6f 72 6c 64>
console.log(buffer.indexOf('l')) // 返回找到的第一个匹配的字节位置
console.log(buffer.indexOf('l', 3)) // 从 buffer 的第4个字节开始查找。
console.log(buffer.indexOf('a')) // -1 。 若找不到返回 -1
includes
判断 buffer 中是否包含指定的数据。类似于数组的 includes 方法。
接收两个参数。第一个是要查找的内容,第二个指定从 buffer 的哪个字节开始查找。若 buffer 中包含,则返回 true,不包含返回false。
let buffer = Buffer.from('hello, world')
console.log(buffer.includes('l')) // true
console.log(buffer.includes('a')) // false
includes
方法和 indexOf
方法的作用是一样的,都是用于查找一个目标数据。
fill
向 buffer 中填充数据。
接收4个参数,第一个是要填充进 buffer 的数据。第二、三个参数分别表示 buffer 接收数据的起始位置。第四个参数设置编码格式,默认为 utf8
。
let buffer = Buffer.alloc(8)
// 将 '123' 填充到 buffer 中,从第三个字节开始填充,直到填满
buffer.fill('123', 2)
console.log(buffer) // <Buffer 00 00 31 32 33 31 32 33>
console.log(buffer.toString()) // 123123
// 将 '123' 填充到 buffer 中,默认填满
buffer.fill('123')
console.log(buffer) // <Buffer 31 32 33 31 32 33 31 32>
console.log(buffer.toString()) // 12312312
write
向 buffer 中写入数据。
接收三个参数。第一个是写入的数据,第二、三个分别是 buffer 接收数据的起始位置。第四个参数设置编码格式,默认为 utf8
。
let buffer = Buffer.alloc(6)
// 将 '123' 写入到 buffer 中,数据有多少就写入多少
buffer.write('123')
console.log(buffer) // <Buffer 31 32 33 00 00 00>
console.log(buffer.toString()) // 123
// 将 '123' 填充到 buffer 中,从第三个字节开始填充,数据有多少写入多少
buffer.write('123', 3)
console.log(buffer) //<Buffer 31 32 33 31 32 33>
console.log(buffer.toString()) // 123123
write
和 fill
的区别:前者是数据有多少往 buffer 中写入多少,而 fill
是重复写入,直到填满整个 buffer。
subarray
从 buffer 中截取数据并返回一个新的 buffer。
在数组和字符串的原型方法中,截取数据的方法叫作 slice。而 Buffer 对象中的 slice 方法已经废弃,改用 subarray
。
该方法接收4个参数,第一个是要填充进 buffer 的数据。第二、三个参数分别表示 buffer 接收数据的起始位置。第四个参数设置编码格式,默认为 utf8
。
let buffer = Buffer.from('123456')
// 截取 buffer 的全部数据
let subBuffer1 = buffer.subarray()
// 截取 buffer 的前三个字节
let subBuffer2 = buffer.subarray(0, 3)
console.log(buffer) // <Buffer 31 32 33 34 35 36>
console.log(subBuffer1) // <Buffer 31 32 33 34 35 36>
console.log(subBuffer2) // <Buffer 31 32 33>
Buffer 的静态方法
Buffer的静态方法我们已经学过三个了,就是用来创建 buffer 的 alloc
、allocUnSafe
from
方法。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DiWOJhEZ-1675356697098)(https://static.kunwu.tech/images/2022-08/202208020753371.webp)]
Buffer.concat
将多个 buffer 拼接成一个新的 buffer。就类似数组中的 concat
方法。用的非常多。
let buffer1 = Buffer.from('昆吾')
let buffer2 = Buffer.from('kw')
console.log(Buffer.concat([buffer1, buffer2]))
// 第二个参数指定生成新的 buffer 的长度
console.log(Buffer.concat([buffer1, buffer2], 7))
// <Buffer e6 98 86 e5 90 be 6b 77>
// <Buffer e6 98 86 e5 90 be 6b>
Buffer.isBuffer
这个很好理解,见名知义,用来判断数据是否是 buffer 类型。
let buffer = Buffer.from('1')
console.log(Buffer.isBuffer(1)) // false
console.log(Buffer.isBuffer(buffer)) // true
总结
本文简单介绍了 Node.js 中 Buffer 对象的使用,包括创建实例 buffer 的方法,以及操作 buffer 的常用方法。
创建 buffer 主要使用两个方法:
- Buffer.alloc:创建一个固定字节大小的内存空间
- Buffer.from:将传入的数据转为二进制放到内存中,用法有很多
操作 buffer 的方法包括 Buffer 原型对象上的实例方法和 Buffer 类的静态方法:
- toString:读取 buffer 中的数据,转为字符串
- copy:拷贝 buffer 中的数据到另一个 buffer 中
- indexOf:查找 buffer 中是否有目标内容
- includes:作用和 indexOf 相同,返回结果不同
- fill:向 buffer 中填充数据
- write:向 buffer 中写入数据
- subarray:从 buffer 中截取数据
- Buffer.concat:拼接 buffer
- Buffer.isBufer:判断 buffer 类型
方法比较多,而且不常用,有印象即可。本文可作为文档使用,以供需要时翻看。