Node.js Buffer用法解读
Buffer是什么?
Buffer作为存在于全局对象上,无需引入模块即可使用,你绝对不可以忽略它。
可以理解Buffer是在内存中开辟的一片区域,用于存放二进制数据。Buffer所开辟的是堆外内存。
Buffer的应用场景有哪些?
流
怎么理解流呢?流是数据的集合(与数据、字符串类似),但是流的数据不能一次性获取到,数据也不会全部load到内存中,因此流非常适合大数据处理以及断断续续返回chunk的外部源。流的生产者与消费者之间的速度通常是不一致的,因此需要buffer来暂存一些数据。buffer大小通过highWaterMark参数指定,默认情况下是16Kb。
存储需要占用大量内存的数据
Buffer对象占用的内存空间是不计算在Node.js进程内存空间限制上的,所以可以用来存储大对象,但是对象的大小还是有限制的。一般情况下32位系统大约是1G,64位系统大约是2G。
如何创建Buffer
除了流自动隐式创建Buffer之外,也可以手动创建Buffer,方式如下:
Buffer中存储的数据已确定
Buffer.from(obj) //obj支持的类型string,buffer,arrayBuffer,array,orarray-likeobject
注意:Buffer.from不支持传入数字,如下所示:
Buffer.from(1234); buffer.js:208 thrownewerrors.TypeError( ^ TypeError[ERR_INVALID_ARG_TYPE]:The"value"argumentmustnotbeoftypenumber.Receivedtypenumber atFunction.from(buffer.js:208:11) ...
若要传入数字可以采用传入数组的方式:
constbuf=Buffer.from([1,2,3,4]); console.log(buf);//
但是这种方式存在一个问题,当存入不同的数值的时候buffer中记录的二进制数据会相同,如下所示:
constbuf2=Buffer.from([127,-1]); console.log(buf2);//constbuf3=Buffer.from([127,255]); console.log(buf3);// console.log(buf3.equals(buf2));//true
当要记录的一组数全部落在0到255(readUInt8来读取)这个范围,或者全部落在-128到127(readInt8来读取)这个范围那么就没有问题,否则的话就强烈不推荐使用Buffer.from来保存一组数。因为不同的数字读取时应该调用不同的方法。
Buffer存储数据未确定
Buffer.alloc、Buffer.allocUnsafe、Buffer.allocUnsafeSlow
Buffer.alloc会用0值填充已分配的内存,所以相比后两者速度上要慢,但是也较为安全。当然也可以通过--zero-fill-buffersflag使allocUnsafe、allocUnsafeSlow在分配完内存后也进行0值填充。
node--zero-fill-buffersindex.js
当分配的空间小于4KB的时候,allocUnsafe会直接从之前预分配的Buffer里面slice空间,因此速度比allocUnsafeSlow要快,当大于等于4KB的时候二者速度相差无异。
//分配空间等于4KB
functioncreateBuffer(fn,size){
console.time('buf-'+fn);
for(vari=0;i<100000;i++){
Buffer[fn](size);
}
console.timeEnd('buf-'+fn);
}
createBuffer('alloc',4096);
createBuffer('allocUnsafe',4096);
createBuffer('allocUnsafeSlow',4096);
//输出
buf-alloc:294.002ms
buf-allocUnsafe:224.072ms
buf-allocUnsafeSlow:209.22ms
functioncreateBuffer(fn,size){
console.time('buf-'+fn);
for(vari=0;i<100000;i++){
Buffer[fn](size);
}
console.timeEnd('buf-'+fn);
}
createBuffer('alloc',4095);
createBuffer('allocUnsafe',4095);
createBuffer('allocUnsafeSlow',4095);
//输出
buf-alloc:296.965ms
buf-allocUnsafe:135.877ms
buf-allocUnsafeSlow:205.225ms
需要谨记一点:newBuffer(xxxx)方式已经不推荐使用了
Buffer使用
buffer转字符串
constbuf=Buffer.from('test');
console.log(buf.toString('utf8'));//test
console.log(buf.toString('utf8',0,2));//te
buffer转json
constbuf=Buffer.from([0x1,0x2,0x3,0x4,0x5]);
console.log(buf.toJSON());//{type:'Buffer',data:[1,2,3,4,5]}
buffer裁剪,裁剪后返回的新的buffer与原buffer指向同一块内存
buf.slice([start[,end]])
- start起始位置
- end结束位置(不包含)
示例:
varbuf1=Buffer.from('test');
varbuf2=buf1.slice(1,3).fill('xx');
console.log("buf2content:"+buf2.toString());//xx
console.log("buf1content:"+buf1.toString());//txxt
buffer拷贝,buffer与数组不同,buffer的长度一旦确定就不再变化,因此当拷贝的源buffer比目标buffer大时只会复制部分的值
buf.copy(target[,targetStart[,sourceStart[,sourceEnd]]])
示例:
varbuf1=Buffer.from('abcdefghijkl');
varbuf2=Buffer.from('ABCDEF');
buf1.copy(buf2,1);
console.log(buf2.toString());//Abcdef
buffer相等判断,比较的是二进制值
buf.equals(otherBuffer)
示例:
constbuf1=Buffer.from('ABC');
constbuf2=Buffer.from('414243','hex');
console.log(buf1.equals(buf2));//true
除了equals之外,compare其实也可以用于判断是否相等(当结果为0则相等),不过compare更主要的作用是用于对数组内的buffer实例排序。
buffer是否包含特定值
buf.includes(value[,byteOffset][,encoding]) buf.indexOf(value[,byteOffset][,encoding])
示例:
constbuf=Buffer.from('thisisabuffer');
console.log(buf.includes('this'));//true
console.log(buf.indexOf('this'));//0
写入读取数值
写入方法:
位数固定且超过1个字节的:write{Double|Float|Int16|Int32| UInt16|UInt32}{BE|LE}(value,offset)
位数不固定的:write{Int|UInt}{BE|LE}(value,offset,bytelength)//此方法提供了更灵活的位数表示数据(比如3位、5位)
位数固定是1个字节的: write{Int8|Unit8}(value,offset)
读取方法:
位数固定且超过1个字节的:read{Double|Float|Int16|Int32|UInt16|UInt32}{BE|LE}(offset)
位数不固定的: read{Int|UInt}{BE|LE}(offset,byteLength)
位数固定是1个字节的:read{Int8|Unit8}(offset)
Double、Float、Int16、Int32、UInt16、UInt32既确定了表征数字的位数,也确定了是否包含负数,因此定义了不同的数据范围。同时由于表征数字的位数都超过8位,无法用一个字节来表示,因此就涉及到了计算机的字节序区分(大端字节序与小端字节序)
关于大端小端的区别可以这么理解:数值的高位在buffer的起始位置的是大端,数值的低位buffer的起始位置则是小端
constbuf=Buffer.allocUnsafe(2); buf.writeInt16BE(256,0) console.log(buf);//buf.writeInt16LE(256,0) console.log(buf);//
http://tools.jb51.net/transcoding/hexconvert这里可以查看数值的不同进制之间的转换,如果是大端的话,则直接按顺序(0100)拼接16进制即可,如果是小端则需要调换一下顺序才是正确的表示方式。
buffer合并
Buffer.concat(list[,totalLength])//totalLength不是必须的,如果不提供的话会为了计算totalLength会多一次遍历
constbuf1=Buffer.from('thisis');
constbuf2=Buffer.from('funny');
console.log(Buffer.concat([buf1,buf2],buf1.length+buf2.length));
//
清空buffer
清空buffer数据最快的办法是buffer.fill(0)
buffer模块与Buffer的关系
Buffer是全局global上的一个引用,指向的其实是buffer.Buffer
constbuffer=require('buffer');
console.log(buffer.Buffer===Buffer);//true
buffer模块上还有其他一些属性和方法
constbuffer=require('buffer');
console.log(buffer);
{Buffer:
{[Function:Buffer]
poolSize:8192,
from:[Function:from],
alloc:[Function:alloc],
allocUnsafe:[Function:allocUnsafe],
allocUnsafeSlow:[Function:allocUnsafeSlow],
isBuffer:[Function:isBuffer],
compare:[Function:compare],
isEncoding:[Function:isEncoding],
concat:[Function:concat],
byteLength:[Function:byteLength],
[Symbol(node.isEncoding)]:[Function:isEncoding]},
SlowBuffer:[Function:SlowBuffer],
transcode:[Function:transcode],
INSPECT_MAX_BYTES:50,
kMaxLength:2147483647,
kStringMaxLength:1073741799,
constants:{MAX_LENGTH:2147483647,MAX_STRING_LENGTH:1073741799}}
上面的kMaxLength与MAX_LENGTH代表了新建buffer时内存大小的最大值,当超过限制值后就会报错
32为机器上是(2^30)-1(~1GB)
64位机器上是(2^31)-1(~2GB)
Buffer释放
我们无法手动对buffer实例进行GC,只能依靠V8来进行,我们唯一能做的就是解除对buffer实例的引用
参考资料
http://cenalulu.github.io/linux/character-encoding/
https://www.nhooo.com/article/31045.htm
http://edu.jb51.net/nodejs/nodejs-buffer.html
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。