前言
在大多数介绍 Buffer 的文章中,主要是围绕数据拼接和内存分配这两方面的。比如我们使用fs模块来读取文件内容的时候,返回的就是一个 Buffer:
在使用net或http模块来接收网络数据时,data事件的参数也是一个 Buffer,这时我们还需要使用Buffer.concat()
来做数据拼接:
还可以利用Buffer.toString()
来做转换base64或十六进制字符的转换,比如:
一般情况下,单个 Node.js 进程是有最大内存限制的,以下是来自官方文档中的说明:
What is the memory limit on a node process?
Currently, by default v8 has a memory limit of 512MB on 32-bit systems, and 1.4GB on 64-bit systems. The limit can be raised by setting --max_old_space_size to a maximum of ~1024 (~1 GB) (32-bit) and ~4096 (~4GB) (64-bit), but it is recommended that you split your single process into several workers if you are hitting memory limits.
由于 Buffer 对象占用的内存空间是不计算在 Node.js 进程内存空间限制上的,因此,我们也常常会使用 Buffer 来存储需要占用大量内存的数据:
以上便是 Buffer 的几种常见用法。然而,阅读 Buffer 的 API 文档时,我们会发现更多的是readXXX()
和writeXXX()
开头的 API,具体如下:
buf.readUIntLE(offset, byteLength[, noAssert])
buf.readUIntBE(offset, byteLength[, noAssert])
buf.readIntLE(offset, byteLength[, noAssert])
buf.readIntBE(offset, byteLength[, noAssert])
buf.readUInt8(offset[, noAssert])
buf.readUInt16LE(offset[, noAssert])
buf.readUInt16BE(offset[, noAssert])
buf.readUInt32LE(offset[, noAssert])
buf.readUInt32BE(offset[, noAssert])
buf.readInt8(offset[, noAssert])
buf.readInt16LE(offset[, noAssert])
buf.readInt16BE(offset[, noAssert])
buf.readInt32LE(offset[, noAssert])
buf.readInt32BE(offset[, noAssert])
buf.readFloatLE(offset[, noAssert])
buf.readFloatBE(offset[, noAssert])
buf.readDoubleLE(offset[, noAssert])
buf.readDoubleBE(offset[, noAssert])
buf.write(string[, offset][, length][, encoding])
buf.writeUIntLE(value, offset, byteLength[, noAssert])
buf.writeUIntBE(value, offset, byteLength[, noAssert])
buf.writeIntLE(value, offset, byteLength[, noAssert])
buf.writeIntBE(value, offset, byteLength[, noAssert])
buf.writeUInt8(value, offset[, noAssert])
buf.writeUInt16LE(value, offset[, noAssert])
buf.writeUInt16BE(value, offset[, noAssert])
buf.writeUInt32LE(value, offset[, noAssert])
buf.writeUInt32BE(value, offset[, noAssert])
buf.writeInt8(value, offset[, noAssert])
buf.writeInt16LE(value, offset[, noAssert])
buf.writeInt16BE(value, offset[, noAssert])
buf.writeInt32LE(value, offset[, noAssert])
buf.writeInt32BE(value, offset[, noAssert])
buf.writeFloatLE(value, offset[, noAssert])
buf.writeFloatBE(value, offset[, noAssert])
buf.writeDoubleLE(value, offset[, noAssert])
buf.writeDoubleBE(value, offset[, noAssert])
这些 API 为在 Node.js 中操作数据提供了极大的便利。假设我们要将一个整形数值存储到文件中,比如当前时间戳为1447656645380,如果将其当作一个字符串存储时,需要占用 11 字节的空间,而将其转换为二进制存储时仅需 6 字节空间即可:
在使用 Node.js 编写一些底层功能时,比如一个网络通信模块、某个数据库的客户端模块,或者需要从文件中操作大量结构化数据时,以上 Buffer 对象提供的 API 都是必不可少的。
接下来将演示一个使用 Buffer 对象操作结构化数据的例子。
操作结构化数据
假设有一个学生考试成绩数据库,每条记录结构如下:
学号 | 课程代码 | 分数 |
---|---|---|
XXXXXX | XXXX | XX |
其中学号是一个 6 位的数字,课程代码是一个 4 位数字,分数最高分为 100 分。
在使用文本来存储这些数据时,比如使用 CSV 格式存储可能是这样的:
100001,1001,99 100002,1001,67 100003,1001,88
其中每条记录占用 15 字节的空间,而使用二进制存储时其结构将会是这样:
学号 | 课程代码 | 分数 |
---|---|---|
3 字节 | 2 字节 | 1 字节 |
每一条记录仅需要 6 字节的空间即可,仅仅是使用文本存储的 40%!下面是用来操作这些记录的程序:
我们可以再编写一段程序来看看效果:
lei-proto 模块介绍
上面的例子中,当每一条记录的结构有变化时,我们需要修改readRecord()
和writeRecord()
,重新计算每一个字段在 Buffer 中的偏移量,当记录的字段比较复杂时很容易出错。为此我编写了lei-proto模块,它允许你通过简单定义每条记录的结构即可生成对应的readRecord()
和`writeRecord()
函数。
首先执行以下命令安装此模块:
$ npm install lei-proto --save
使用lei-proto模块后,前文的例子可以改为这样:
运行与上文同样的测试程序,可看到其结果是一样的:
总结