Buffer
Buffer 对象类似于数组,每一个元素都是16进制的两位数,即每一个元素可以表示一个 0-255 的值。在不同的编码下,字符串的每一个字符占用的元素个数不相同,在 UTF-8 编码下,每一个中文字占 3 个元素,字母和半角标点符号占1个元素。
Buffer 的实例可以先指定大小后赋值,也可以像数组那样使用 index 访问该值,返回一个十进制的数字。
1 | let str = 'ray is handsome' |
此外如果对 buf 某一位赋值不是在0-255之间的数字,那么会根据大小,依次 加/减 256 直到满足是在0-255区间,如果赋值是小数,那么取整。
1 | let buf2 = new Buffer(100) |
一个buffer可以和字符串相互转化,如果没有指定编码,默认编码规则为‘utf-8’。
1 | let buf3 = new Buffer('ray')//let buf = new Buffer(str,[encoding]) |
此外,可以指定buffer的不同段使用不同的编码方式,但是需要注意的是每种编码所用的buffer字节数不一致,且在转回字符串是要分别对每一段指定不同的编码
Buffer 支持的编码格式有限(不支持GBK):
- UTF-8
- ASCII
- Base64
- Binary
- Hex
- UTF-16LE
Buffer 转中文出现乱码的问题
前面提到过,中文在 utf-8 编码之下是三个buffer元素表示一个中文字,在一个读取流操作中,Buffer是被分成一段一段进行传输和读取的,有可能在一个很大的文字文件中会出现一种情况,就是被截取传输的一段 <Buffer >
不能够被 3 整除那么就会出现中文乱码的问题了
1 | const fs = require('fs') |
打印结果如下:1
2
3
4
5
6
7
8
9
10
11
12<Buffer e8 8b 9f e5>
<Buffer 88 a9 e5 9b>
<Buffer bd e5 ae b6>
<Buffer e7 94 9f e6>
<Buffer ad bb e4 bb>
<Buffer a5 2c e5 b2>
<Buffer 82 e5 9b a0>
<Buffer e7 a5 b8 e7>
<Buffer a6 8f e9 81>
<Buffer bf e8 b6 8b>
<Buffer e4 b9 8b 21>
苟�����家生�����,��因祸�����趋之!
在创建可读流的时候通过传入选项 highWaterMark
限定一个chunk 四个buffer字节的方式模拟大文件有可能的截取的不是3的倍数,可见出现了 �
乱码。
Tips :这个highWaterMark
的其实是很有讲究的,node默认是 64KB。首先应该说明的是 fs.createReadStream()
的工作方式:创建可读流内部是在内存中先准备一段 Buffer 然后通过执行 fs.read()进行系统调用读取字节之后复制进准备好的Buffer对象中,Buffer再进行分割成一个一个小的chunk,触发“data”事件。那个在内存中先准备的Buffer就是和highWaterMark
的设置有很大的关系,读取的数据比准备好的 Buffer 大,那就要再一次分配一个新的 Buffer ,并且触发“data”事件。所以把highWaterMark
设置的太小就会相应的影响读取速度。
应对策略
- 创建可读流的时候通过设置编码,让可读流内部调用,decoder 对buffer 先进行解码,之后在监听
data
事件里的chunk 是已经解码好的字符串,decoder 设置了编码规则之后就会知道对于utf-8
编码规则下,是三个字组成一个中文,在不能构成3的倍数的时候,会把字节顺延至下一个buffer对象,拼接之后达到满足条件的情况之后再传给 chunk
1 | const fs = require('fs') |
打印结果:
1 | 苟 |
总的触发 data 事件的次数不变,但是内部调用了 decoder 之后就会更加的 “智能” 一点。
Buffer.concat(list[, totalLength])
Buffer 类提供了一个方法,用来拼接几个 buffer 实例,其中 list是几个buffer实例组成的数组, totalLength 是可选参数,表示数组中每个buffer实例的总和,虽然是可选参数,但是如果不传的话,concat 内部实现时会再一次循环每一个buffer实例计算总长度,所以能传还是尽量传。
1 | const fs = require('fs') |
用这种拼接buffer实例之后,再进行解码,避免了直接凭借字符串时,一个字被截断的现象。
后记
在《深入浅出Nodejs》中Buffer章节做了一个测试,在网络IO中直接传字符串和转化成Buffer之后再传的压力测试做比较,测试结果表明把那些不需要改动的静态文件转化成 Buffer 的形式传输能够明显提升性能。
参考:
NodeJs V8.9.4官方文档
《深入浅出NodeJS》–朴灵
本文为原创文章作为学习交流笔记,如有错误请您评论指教
转载请注明来源:https://isliulei.com/article/node-Buffer/