您的位置:首页 > Web前端 > Node.js

Node.js——理解Buffer

2017-12-09 00:48 148 查看
Buffer是用来操作字节的类似Array的对象,在Node的中为了满足处理网络协议、操作数据库、上传下载文件、网络流的二进制数据等等需求。在Node进程启动的时候全局global就已经加载了Buffer,所以我们并不需要require()

Buffer类似数组,而且每个元素为十六进制的两位数,也是0~255之间的数值,例如:

// Node环境下运行
var str = "床前明月光"
var buffer = new Buffer(str, 'utf-8') // 第二个参数不设置默认 utf-8
console.log(buffer)
// =><Buffer e5 ba 8a e5 89 8d e6 98 8e e6 9c 88 e5 85 89>


当然我们也可以自己给Buffer的元素赋值,注意:Buffer的元素值一定是0~255之间的整数,有小数的话舍弃掉。所以我们赋值:

buffer[3] = -100 // => buffer[3] = 156


依次+256或者-256获得值。

Buffer的内存并不是V8引擎的堆内存,是Node的C++层面申请分配,JavaScript执行分配,采用slab分配机制,其临界值为8KB,当Buffer小于8KB也就是8*1024时是存储在一个slab单元的,多个Buffer需要存储的时候就会查询当前slab剩余空间是否足够否则会创建一个新的slab的容器进行存储。例如:

new Buffer(1); // => 占用一个slab
new Buffer(8192); // => 占用一个slab


第一个Buffer不释放的话其实是占用了8KB,当Buffer超过一个slab的容量时就会被存放在大的SlowBuffer的容器里,这个容器被这个大的Buffer独占。

Buffer可以转换为ASCII、UTF-8、UTF-1LE/UCS-2、Base64、Binary、Hex编码类型的字符串,同时Buffer提供Buffer.isEncoding(encoding)方法判断是否支持转换。如果为false,我们可以去npm找相关的模块进行转换。Buffer是不支持GBK、GB2312等中文相关编码的。

我们创建一个txt文件,文件的内容为:

床前明月光,疑是地上霜。举头望明月,低头思故乡。


然后进行读取操作:

var fs = require('fs')

var buf = fs.createReadStream('./test.txt', {highWaterMark: 11})
var data = ''
buf.on("data", (chunk) => {
console.log(chunk)
data += chunk
})
buf.on("end", () => {
console.log(data)
})


注意这里的data += chunk实际为

data = data.toString() + chunk.toString()


以上是一个很简单的文件读取方法,在这儿由于一个中文字符占用三个buffer元素,所以当我们限制Buffer的长度为11时的结果为:

<Buffer e5 ba 8a e5 89 8d e6 98 8e e6 9c>
<Buffer 88 e5 85 89 ef bc 8c e7 96 91 e6>
<Buffer 98 af e5 9c b0 e4 b8 8a e9 9c 9c>
<Buffer e3 80 82 e4 b8 be e5 a4 b4 e6 9c>
<Buffer 9b e6 98 8e e6 9c 88 ef bc 8c e4>
<Buffer bd 8e e5 a4 b4 e6 80 9d e6 95 85>
<Buffer e4 b9 a1 e3 80 82>
床前明��光,疑���地上霜。举头��明月,���头思故乡。


出现了乱码,分析原因可知是每个buffer最大值为11导致的。依第一个Buffer为例,由于三个元素构成一个中文字符,导致读取到第四个字符时只剩两个元素,导致乱码,在这儿size为11只是人为实验,在实际开发的过程中宽字节字符串有可能存在被截断的情况,所以该问题不容忽视。

我们在读取的时候设置setEncoding方法

var buf = fs.createReadStream('./test.txt', {highWaterMark: 11})
buf.setEncoding('utf8')


重新执行程序结果为:

床前明
月光,疑
是地上霜
。举头
望明月,
低头思故
乡。
床前明月光,疑是地上霜。举头望明月,低头思故乡。


在我们设置setEncoding的时候,可读取流对象在内部设置了一个decoder对象,在得知为utf8转码方式时就会保留不完整的编码,与下一Buffer进行拼接。使得编码问题解决。这样就解决了大部分问题。但是setEncoding只支持ASCII、UTF-8、UTF-1LE/UCS-2的编码,对于其他编码我们需要使用Buffer的内建的concat方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: