在JS中,Buffer是Uint8Array类的子类,因此有许多数组方法可以调用。
Buffer对象有length属性,可以通过下标赋值。
一些是一个Buffer对象 <Buffer e6 b7 b1 e5 85 a5 e6 b5 85 e5 87 ba 6e 6f 64 65 2e 6a 73>
Buffer对象是由一系列的两位数元素组成。其中每个元素是由两个16进制的数组成。在UTF-8中文字占3个元素。因此很容易造成文件流的方式读取文字然后转码的时候导致乱码。
Buffer对象的分配
-
小Buffer(以8KB为界):预先申请,事后分配
系统先分配一个slab对象,该对象可以含有多个Buffer对象,slab空间不够时候会再申请一个新的slab。原slab中剩余的空间会造成浪费。 而slab中的小Buffer对象们完全被释放,slab才会被回收掉
-
大Buffer:直接使用C++层面提供的内存
一个slab对一个Buffer对象
中文文本读取乱码原因
下方的示例代码中读取的chunk为Buffer对象,每次读取11个元素组成一个chunk,而data事件中存在 data+=chunk,其等价于 data = data.toString() = chunk.toString()。toString方法默认是以UTF-8为编码。在UTF-8编码中中文的字符为3,因此在读取的时候如果chunk含有元素的个数不是3的倍数,那么截断的字符无法识别就出现了乱码。
var fs = require('fs')
var rs = fs.createReadStream('test.md',{highWaterMark: 11})
var data = ""
rs.on('data', function(chunk){
data += chunk
})
res.on('end', function(){
console.log(data)
})
解决方法
1. setEncoding()与string_decoder()
给文件读取流设置字符。
var fs = require('fs')
var rs = fs.createReadStream('test.md',{highWaterMark: 11})
rs.setEncoding(''utf8)
var data = ""
rs.on('data', function(chunk){
data += chunk
})
res.on('end', function(){
console.log(data)
})
原理: 调用内置decoder对象(其为string_decoder模块StirngDecoder的实例对象)。在每次读取的文件流后会自动保存此次被截断的元素,然后与下次的元素拼接在一起。 缺点: 只能处理UTF-8,BASE64,UCS-2/UTF-16LE这三种方法
2. 利用Buffer.concat将文件流拼接成一个整体的Buffer对象,然后转码。
var chunks = []
var size = 0
res.on('data', function(chunk){
chunks.push(chunk)
size = chunk.length
})
res.on('end', function(){
var buf = Buffer.concat(chunks, size)
var str = iconv.decode(buf, 'utf8')
console.log(str)
})
Buffer对象的性能
利用二进制数据代替字符串传输可以提高网络吞吐率。
fs.createReadStream(path, opts)
opts中存在highWaterMark对象, highWaterMark的数值越大,一次读取的字符越多,触发data的次数越小; 该方法会在内存中准备一段Buffer,如果该Buffer对象剩余小于128字节用完,会再次申请一个highWaterMark大小的Buffer对象