当前位置: 首页 > 后端技术 > Node.js

Nodejs进阶:核心模块Buffer常用API使用总结

时间:2023-04-03 10:35:59 Node.js

本文节选自《Nodejs学习笔记》,更多章节及更新请访问github主页地址。欢迎加入群交流,群号197339705。模块概述Buffer是node的核心模块。开发者可以用它来处理二进制数据,比如读写文件流,处理网络请求数据等等。Buffer有很多API。本文仅选取比较常用/通俗易懂的API进行讲解,包括Buffer实例的创建、比较、连接、复制、查找、遍历、类型转换、拦截、编码转换等。CreatenewBuffer(array)Buffer.alloc(length)Buffer.allocUnsafe(length)Buffer.from(array)vianewBuffer(array)//创建一个包含字符串'buffer'的ASCII字节的新Bufferconstbuf=new缓冲器([0x62、0x75、0x66、0x66、0x65、0x72]);验证:vararray='buffer'.split('').map(function(v){return'0x'+v.charCodeAt(0).toString(16)});console.log(array.join());//输出:0x62,0x75,0x66,0x66,0x65,0x72通过Buffer.alloc(length)varbuf1=Buffer.alloc(10);//长度为10的缓冲区,初始值为0x0varbuf2=Buffer.alloc(10,1);//长度为10的缓冲区,初始值为0x1varbuf3=Buffer.allocUnsafe(10);//长度为10的缓冲区,初始值不确定varbuf4=Buffer.from([1,2,3])//长度为3的缓冲区,初始值为0x01,0x02,0x03通过Buffer.from()例子1:缓冲器。from(array)//[0x62,0x75,0x66,0x66,0x65,0x72]是字符串"buffer"//0x62是十六进制,转十进制就是98,代表字母bvarbuf=Buffer.from([0x62,0x75,0x66,0x66,0x65,0x72]);console.log(buf.toString());例2:Buffer.from(string[,encoding])通过string创建一个buffer,并将buffer转为字符串的时候,记得保持编码一致,否则会出现乱码,如下显示varbuf=Buffer.from('thisisatést');//默认使用Utf8//输出:这是一个téstconsole.log(buf.toString());//默认编码为utf8,所以正常打印//Output:thisisatC)stconsole.log(buf.toString('ascii'));//转换为字符串时,编码不是utf8,所以乱码分析如下:varletter='é';varbuff=Buffer.from(字母);//默认编码为utf8,这里占用两个字节varlen=buff.length;//2var代码=buff[0];//第一个字节是0xc3,也就是195:超出了ascii的最大支持范围varbinary=code.toString(2);//195二进制:10101001varfinalBinary=binary.slice(1);//丢弃高位1并变为:0101001varfinalCode=parseInt(finalBinary,2);//0101001对应的十进制:67varfinalLetter=String.fromCharCode(finalCode);//67对应的字符:C//同理,转换为0xa9的ascii字符为)//因此,最终输出为thisisatC)st例3:Buffer.from(buffer)创建了一个新的Buffer实例,并将缓冲区数据复制到新实例。varbuff=Buffer.from('buffer');varbuff2=Buffer.from(buff);console.log(buff.toString());//输出:bufferconsole.log(buff2.toString());//输出:bufferbuff2[0]=0x61;console.log(buff.toString());//输出:bufferconsole.log(buff2.toString());//输出:aufferbuffer比较buf.equals(otherBuffer)判断两个buffer实例中存储的数据是否相同,如果是则返回true,否则返回false。//示例1:相同的编码,相同的内容varbuf1=Buffer.from('A');varbuf2=Buffer.from('A');console.log(buf1.equals(buf2));//true//示例2:代码相同但内容不同varbuf3=Buffer.from('A');varbuf4=Buffer.from('B');console.log(buf3.equals(buf4));//false//例3:编码不同,内容相同varbuf5=Buffer.from('ABC');//varbuf6=Buffer.from('414243','hex');console.log(buf5.equals(buf6));buf.compare(target[,targetStart[,targetEnd[,sourceStart[,sourceEnd]]]])也是比较两个buffer实例,区别是:可以指定具体比较range(由start和end指定)返回值为整数,buf和target的大小关系假设返回值为0:buf和target大小相同。1:buf大于target,也就是说buf应该排在target之后。-1:buf小于target,也就是说buf应该排在target之前。看例子,官方的例子还不错,直接贴吧:constbuf1=Buffer.from('ABC');constbuf2=Buffer.from('BCD');constbuf3=Buffer.from('ABCD');//打印:0console.log(buf1.compare(buf1));//打印:-1console.log(buf1.compare(buf2));//打印:-1console.log(buf1.compare(buf3));//打印:1console.log(buf2.compare(buf1));//打印:1console.log(buf2.compare(buf3));//打印:[,,]//(这个结果等于:[buf1,buf3,buf2])console.log([buf1,buf2,buf3].sort(Buffer.compare));Buffer.compare(buf1,buf2)类似于buf.compare(target),一般用于排序。直接贴官方例子:constbuf1=Buffer.from('1234');constbuf2=Buffer.from('0123');constarr=[buf1,buf2];//打印:[,]//(这个结果等于:[buf2,buf1])console.log(arr.sort(Buffer.compare));从这里开始Buffer.from([62])稍微研究一下Buffer.from(array)。以下是官方文档中对该API的描述,即数组的每个元素对应1个字节(8位),取值范围为0~255。使用八位字节数组分配新的Buffer。数组元素是数字首先我们来看一下传入元素是数字的场景。下面分别是十进制、八进制、十六进制,与预期结果一致。varbuff=Buffer.from([62])////buff[0]===parseInt('3e',16)===62varbuff=Buffer.from([062])////buff[0]===parseInt(62,8)===parseInt(32,16)===50varbuff=Buffer.from([0x62])////buff[0]===parseInt(62,16)===98数组元素为字符串我们来看一下传入元素为字符串的场景。parseInt('062')时,以0开头的字符串可以解释为62或50(八进制)。这里我们看到采用了第一种解释。字符串的场景和parseInt()有没有关系还没有深入探讨,只是猜测而已。TODO(找时间研究)varbuff=Buffer.from(['62'])////buff[0]===parseInt('3e',16)===parseInt('62')===62varbuff=Buffer.from(['062'])////buff[0]===parseInt('3e',16)===parseInt('062')===62varbuff=Buffer.from(['0x62'])////buff[0]===parseInt('62',16)===parseInt('0x62')===98数组元素的大小超过1个字节。感兴趣的同学可以自行探索。varbuff=Buffer.from([256])//Buffer.from('1')会不自觉地将Buffer.from('1')[0]等同于开头的“1”,其实,“1”对应的代码是49。varbuff=Buffer.from('1')//console.log(buff[0]===1)//false所以你可以知道比较,code为1是控制字符,表示标题开始。console.log(String.fromCharCode(49))//'1'console.log(String.fromCharCode(1))//'\u0001'bufferconnection:Buffer.concat(list[,totalLength])备注:个人意见totalLength参数非常多余。从官方文档来看,是从性能提升的角度考虑的。但是内部实现只是遍历list,将长度相加得到totalLength。从这个角度来看,性能优化几乎可以忽略不计。varbuff1=Buffer.alloc(10);varbuff2=Buffer.alloc(20);vartotalLength=buff1.length+buff2.length;控制台日志(总长度);//30varbuff3=Buffer.concat([buff1,buff2],totalLength);console.log(buff3.length);//30除了上面提到的性能优化,totalLength还有两点需要注意。假设list中所有buffer的长度累加和为lengthtotalLength>length:返回一个长度为totalLength的Buffer实例,超出长度的部分用0填充。totalLengthvarbuff7=Buffer.concat([buff4,buff5],3);console.log(buff7.length);//3console.log(buff7);//copy:buf.copy(target[,targetStart[,sourceStart[,sourceEnd]]])使用比较简单,如果忽略最后三个参数,则将buf中的数据复制到目标,如下所示:varbuff1=Buffer.from([1,2]);varbuff2=Buffer.alloc(2);buff1.copy(buff2);console.log(buff2);//其他三个参数比较直观,直接看官方例子constbuf1=Buffer.allocUnsafe(26);constbuf2=Buffer.allocUnsafe(26).fill('!');for(leti=0;i<26;i++){//97是'a'的十进制ASCII值buf1[i]=i+97;}buf1.copy(buf2,8,16,20);//打印:!!!!!!!!!!!!!!!!!!!!!console.log(buf2.toString('ascii',0,25));查找:buf.indexOf(value,byteOffset)和array查找类似。需要注意的是,该值可以是任意类型的String、Buffer或Integer。String:如果是字符串,那么encoding就是它对应的编码,默认是utf8。Buffer:如果是Buffer实例,那么会将value中的完整数据与buf进行比较。Integer:如果是数字,该值将被视为一个无符号的8位整数,取值范围为0到255。另外,起始搜索位置可以通过byteOffset指定。直接上代码,官方例子妥妥的,耐心看完基本就能看懂了。constbuf=Buffer.from('thisisabuffer');//打印:0console.log(buf.indexOf('this'));//打印:2console.log(buf.indexOf('is'));//打印:8console.log(buf.indexOf(Buffer.from('abuffer')));//打印:8//(97是'a'的十进制ASCII值)console.log(buf.indexOf(97));//打印:-1console.log(buf.indexOf(Buffer.from('abufferexample')));//打印:8console.log(buf.indexOf(Buffer.from('缓冲区示例').slice(0,8)));constutf16Buffer=Buffer.from('\u039a\u0391\u03a3\u03a3\u0395','ucs2');//打印:4console.log(utf16Buffer.indexOf('\u03a3',0,'ucs2'));//打印:6console.log(utf16Buffer.indexOf('\u03a3',-4,'ucs2'));写入:buf.write(string[,offset[,length]][,encoding])将sring写入buf实例,并返回写入的字节数。参数如下:string:写入的字符串。offset:从buf的第1位开始写入,默认为0。length:写入多少字节,默认为buf.length-offset。encoding:字符串的编码,默认为utf8。看一个简单的例子varbuff=Buffer.alloc(4);buff.write('a');//返回1console.log(buff);//打印buff.write('ab');//返回2console.log(buff);//printfill:buf.fill(value[,offset[,end]][,encoding])用值填充buf,常用于初始化buf。参数说明如下:value:用来填充的内容,可以是Buffer、String或Integer。offset:从哪一位开始填充,默认为0。end:停止填充的位置,默认为buf.length。encoding:如果value为String,则为value的编码,默认为utf8。示例:varbuff=Buffer.alloc(20).fill('a');console.log(buff.toString());//将AAAAAAAAAAAAAAAAAAAAA转换为字符串:buf.toString([[sending[,start[,start[,end]])将buf解码为字符串,用法更直观,请参见示例varvarbuff=buffer=buffer.from.from('hellohello'');console.log(buff.toString());//helloconsole.log(buff.toString('utf8',0,2));//将he转换为JSON字符串:buf.toJSON()varbuff=Buffer.from('hello');console.log(buff.toJSON());//{type:'Buffer',data:[104,101,108,108,111]}遍历:buf.values(),buf.keys(),buf.entries()用于执行...关于buf的遍历,直接看例子。varbuff=Buffer.from('abcde');for(constkeyofbuff.keys()){console.log('keyis%d',key);}//key是0//key是1//keyis2//keyis3//keyis4for(constvalueofbuff.values()){console.log('valueis%d',value);}//valueis97//valueis98//值为99//值为100//值为101for(constpairofbuff.entries()){console.log('buff[%d]===%d',pair[0],pair[1]);}//buff[0]===97//buff[1]===98//buff[2]===99//buff[3]===100//buff[4]===101拦截:buf.slice([start[,end]])用于拦截buf,返回一个新的Buffer实例。需要注意的是这里返回的Buffer实例仍然指向buf的内存地址,所以修改新的Buffer实例也会影响buf。varbuff1=Buffer.from('abcde');console.log(buff1);//<缓冲区6162636465>varbuff2=buff1.slice();console.log(buff2);//<缓冲区6162636465>varbuff3=buff1.slice(1,3);console.log(buff3);//<缓冲区6263>buff3[0]=97;//parseInt(61,16)==>97console.log(buff1);//TODOcreate,copy,intercept,convert,searchbuffer,arraybuffer,dataview,typedarraybuffervsencodingBuffer.from(),Buffer.alloc(),Buffer.alocUnsafe()BuffervsTypedArray动态文档总结缓冲区内存空间的分配Buffer类的实例类似于整数数组,但对应于V8堆之外的固定大小的原始内存分配。Buffer的大小在创建时就确定了,不能调整大小。相关链接unicode对照表https://unicode-table.com/cn/...字符编码注释:ASCII、Unicode和UTF-8http://www.ruanyifeng.com/blo...