共计 1999 个字符,预计需要花费 5 分钟才能阅读完成。
概念
buffer 存了什么
buffer 是一个操作字节的对象,它的底层是一个字节数组,存储着 16 进制数字。
var str = 'hello buffer'
var buffer = new Buffer(str, 'utf-8')
console.log(buffer) // 输出的是十六进数字
buffer 的每个元素是 16 进制的两位数,也就是每个元素的大小是 0 -255.
因为 F X 16 + F X 16^0 = 255
溢出了怎么办
我们可以直接对 buffer 的元素进行赋值
var buffer = new Buffer(10)
buffer[0] = 300
console.log(buffer[0])
如果赋的值是小数,小数部分会直接被舍弃。
溢出的时候,就是如果大于 255 就减去 256 直到小于 255,如果小于 0 则加上 256 直到大于 0
if (x > 255) {while(x > 255) {x = x - 256}
}
if (x < 0) {x = x + 256}
补充:~~~~
负数在计算机里面存储的是补码,最高位为符号位(0 为正,1 为负),除符号位,其他位会取反,最低位取反后加 1
如:-1
取反前:1000 0001
取反:1111 1110
末位加 1:1111 1111
这样计算机读出来就是 255 啦
字符串与 Buffer 的转换
字符串跟 buffer 可以直接转换的编码类型有:
ASCII; UTF-8; UTF-16LE/UCS-2; Base64; Binary; Hex;
对于不支持的,可以用第三方库协助编码解码,常用的有:
iconv:调用 c ++ 实现,需要 js 到 c ++ 的切换,要消耗更多性能。
iconv-lite:纯 Js 实现,效率更高。
基本转换
var buffer = new Buffer(str, [enconding])
buffer.toString([enconding])
分段转换
注意同一段编解码要用同样的 enconding
var buffer = Buffer.write(str, [offset], [length], [enconding])
buffer.toString([enconding], [start], [end])
库 iconv/iconv-lite 的使用
由于我们常用的 GBK,GB2312 都不在 Buffer 默认支持的编码列表中,我们需要借助第三方库进行编码解码。
库 | 实现 | 处理无法转换内容 | 总结 |
---|---|---|---|
iconv-lite | js 实现 | 直接输出乱码 | 不需要从 c ++ 切换到 js,性能比较好 |
iconv | 调用 c ++ 实现 | 提供忽略或翻译处理 | 对乱码的处理比较完善 |
var iconv = require('iconv-lite');
var buffer = iconv.encode('床前明月光', 'GBK');
var str = iconv.decode(buffer, 'GBK')
buffer 的拼接:乱码问题的解决
读取文件时,onData 里面拿到的也是 buffer, 直接用加号拼接 buffer,在长字节输入时,容易产生乱码。
var fs = require('fs')
var rs = fs.createReadStream('test.md', {highWaterMark: 11})
var data = ''rs.on('data', function(chunk) {data += chunk})
rs.on('end', function() {console.log(data) // 输出存在乱码,因为一个汉字 3 个字节,不能被 11 整除,第三个字只能由两个字节显示,会出现乱码
})
正确的拼接方式是用一个数组把每次读取的 chunk 存储起来,然后用 buffer.concat 生成一个合并的 Buffer 对象。
var fs = require('fs')
var iconv = require('iconv-lite');
var rs = fs.createReadStream('test.md', {highWaterMark: 11})
var chunks = []
var size = 0
rs.on('data', function(chunk) {chunks.push(chunk)
size += chunk.length;
})
rs.on('end', function() {var buffer = Buffer.concat(chunks, size)
var str = iconv.decode(buffer, 'utf-8')
console.log(str)
})
buffer 的性能优势
在网络传输中,如果先把传输的对象转换成 buffer 可以提高系统的性能。
对于文件操作, 文件本身存储的就是二进制数据,所以在不需要改变文件内容的场景下,直接传输 Buffer 性能最好。另外 highWaterMark 的值会影响性能,highWaterMark 设置过小,会导致读取次数过多,设置过大,又可能导致读取小文件的时候浪费内存空间(这个申请多了的内存还是可以给下一次读取使用)。对大问题的读取,设置比较大的 highWaterMark 可以提高性能。