buffer基础知识

Buffer 类是一个全局变量类型,用来直接处理2进制数据的,JS语言自身只有字符串数据类型,没有二进制数据类型,而在处理TCP和文件流的时候,必须要处理二进制数据,NodeJS提供Buffer对象来对二进制数据的操作。

创建Buffer实例

使用Buffer.from(), Buffer.alloc(), Buffer.allocUnsafe()等方法来创建一个Buffer实例,6.0版本以前直接使用构造函数创建的方法new Buffer()已被丢弃,不推荐使用,因为有可能会造成内存泄漏。

Buffer.from(value,[…])

Buffer.from(value,[…])在转换到时候分为四种情况:
1、第一,value为16进制数组,将数组转化为buffer,如果不是16进制,则会进行转换,代码如下:

1
2
var buf = Buffer.from([2,9,7,8,17]);
console.log(buf);//<Buffer 02 09 07 08 11>

2、value为字符串,则转换字符串为buffer,该方法会使用buffer池,代码如下:

1
2
var buf = Buffer.from('hello');
console.log(buf);//<Buffer 68 65 6c 6c 6f>

3、value为buffer实例,则将value拷贝至新的buffer中,这里只是值的拷贝,不会共享内存,代码如下;

1
2
3
4
5
6
7
var buf1 = Buffer.from('hello');
var buf2= Buffer.from(buf1);
console.log(buf1);//<Buffer 68 65 6c 6c 6f>
console.log(buf2);//<Buffer 68 65 6c 6c 6f>
buf2[0]=0x56;
console.log(buf1);//<Buffer 68 65 6c 6c 6f>
console.log(buf2);//<Buffer 56 65 6c 6c 6f>

4、value为arrayBuffer时,还有两个可选参数(value,byteOffset,length),byteOffset指定从arrayBuffer开始复制的位置,length复制的长度,代码如下;

1
2
3
4
5
var arr = new Uint8Array(2);
arr[0] = 69;
arr[1] = 67;
var buf = Buffer.from(arr.buffer,0,2);
console.log(buf);//<Buffer 45 43>

注意:如果引用的是arr.buffer,则新创建的buffer, buf与arr共享内存,任意改变buf和arr另一个随之改变,代码验证如下:

1
2
3
4
5
6
7
8
9
10
11
12
var arr = new Uint8Array(2);
arr[0] = 69;
arr[1] = 67;
var buf = Buffer.from(arr.buffer);
console.log(buf);//<Buffer 45 43>
console.log(arr);//Uint8Array [ 69, 67 ]
buf[0]=123;
console.log(buf);//<Buffer 7b 43>
console.log(arr);//Uint8Array [ 123, 67 ]
arr[0]=88;
console.log(buf);//<Buffer 58 43>
console.log(arr);//Uint8Array [ 88, 67 ]

Buffer.alloc(size, fill, encoding)

Buffer.alloc(size, fill, encoding),参数含义如下:

size,指定buffer的长度,但不能超过buffer.kMaxLength,若不是数字则报错
fill,指定初始化buffer的值,默认为0
encoding,如果fill是字符串,则该参数指定fill的编码

代码如下所示:

1
2
3
4
5
6
7
8
var buf1=Buffer.alloc(5);
console.log(buf1);//-><Buffer 00 00 00 00 00>
var buf2=Buffer.alloc(5,'hello','base64');
console.log(buf2);//-><Buffer 85 e9 65 85 e9>
var buf3=Buffer.alloc(5,'hello').toString();
console.log(buf3);//->hello
var buf4=Buffer.alloc(5,'hello');
console.log(buf4);//-><Buffer 68 65 6c 6c 6f>

Buffer.allocUnsafe(size)
Buffer.allocUnsafe(size),size参数指定buffer的大小,该方法返回一个没有初始化的buffer,因此可能还保留有敏感的数据,造成信息的泄漏,建议使用buffer.fill(0)函数初始化buffer,该方法与Buffer.alloc(size, fill)是不一样的,有可能使用8KB池。代码如下:

1
2
3
4
var buf1=Buffer.allocUnsafe(5);
console.log(buf1);//-><Buffer 02 00 00 00 bf>
buf1.fill(0);
console.log(buf1);//-><Buffer 00 00 00 00 00>

Buffer.allocUnsafeSlow(size)
Buffer.allocUnsafeSlow(size),参数含义同上,该方法不会使用Buffer池,容易造成内存的浪费;代码如下:

1
2
3
4
var buf1=Buffer.allocUnsafeSlow(5);
console.log(buf1);//-><Buffer 48 37 4b 64 bc>
buf1.fill(0);
console.log(buf1);//-><Buffer 00 00 00 00 00>

buffer解码

使用buf.toString([encoding[, start[, end]]])方法将buffer转换成字符串,encoding指定字符编码,默认为’utf8’,start开始位置,end结束位置(不包括),目前encoding只支持’ascii,utf8,utf16le,ucs2,base64,latin1,binary,hex’,使用如下所示:

1
2
3
4
5
var buf = Buffer.from('hello');
console.log(buf.toString('base64'));//aGVsbG8=
console.log(buf.toString('utf8'));//hello
console.log(buf.toString('utf8',2,6));//llo
console.log(buf.toString('hex'));//68656c6c6f

buffer拼接、复制、填充、分割

‘填充’

方法buf.fill(value[, offset[, end]][, encoding])使用指定的值填充buffer,参数offset指定填充的起始位置,end为结束位置,使用如下所示:

1
2
3
4
console.log(Buffer.allocUnsafe(5).fill('a',0,5,'base64'));//<Buffer 78 d3 d0 75 68>
console.log(Buffer.allocUnsafe(5).fill('a',0,5,'utf8'));//<Buffer 61 61 61 61 61>
console.log(Buffer.allocUnsafe(5).fill('a').toString('utf8'));//aaaaa
console.log(Buffer.allocUnsafe(5).fill('a',2,5).toString('utf8'));// aaa

拼接

方法Buffer.concat(list[, totalLength])将多个buffer合并在一起,并返回一个新的buffer实例,参数totalLength为指定的buffers的长度总和,如果不提供该值,函数内部会循环去获取每一个buffer的长度,然后进行拼接,因此为了速度,最好指定一个总长度,使用如下:

1
2
3
4
5
6
7
8
9
10
11
12
function bufferInjoin(buffArr){
var len = 0;
buffArr.forEach(function(buff,index,arr){
len+=buff.length;
});
var buffer = Buffer.concat(buffArr,len);
return buffer;
}
var buff = bufferInjoin([Buffer.from('hehe'),Buffer.allocUnsafe(5).fill('a')]);
console.log(buff);//<Buffer 68 65 68 65 61 61 61 61 61>
console.log(buff.length);//9
console.log(buff.toString());//heheaaaaa

复制
方法buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])可以实现buf到target的复制,参数含义如下:

  • target,复制目标
  • targetStart,复制目标开始被覆盖的位置
  • sourceStart,复制源开始复制的位置
  • sourceEnd,复制源复制结束的位置

使用如下所示:

1
2
3
4
const buf1 = Buffer.from('hello world!');
const buf2 = Buffer.allocUnsafe(5).fill('x');
buf1.copy(buf2,0,0,5);
console.log(buf2.toString());//hello

分割
方法buf.slice([start[, end]])可以分割buffer,返回一个新的buffer,但是仍然是引用原buffer,因此改变原buffer数据,该新buffer也会跟着改变,如果参数start,end为负数,则先要加上buffer的长度再进行计算,如下所示:

1
2
3
4
5
6
7
const buf1 = Buffer.from('hello world.');
const buf2 = buf1.slice(0);
console.log(buf2);//<Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64 2e>
buf2[0] = 88;
console.log(buf1);//<Buffer 58 65 6c 6c 6f 20 77 6f 72 6c 64 2e>
const buf3 = buf1.slice(-6,-1);
console.log(buf3.toString());//world

buffer读写

buf.write(string[, offset[, length]][, encoding]),向buffer写入字符串
1
2
3
4
var buf=Buffer.alloc(10);
buf.write("hello",2,7,'utf8');
console.log(buf);//-><Buffer 00 00 68 65 6c 6c 6f 00 00 00>
console.log(buf.read());//-><Buffer 00 00 68 65 6c 6c 6f 00 00 00>
buffer写操作通过write开头的写api来完成,主要有以下这些:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
buf.write(string[, offset[, length]][, encoding]),向buffer写入字符串
buf.writeDoubleBE(value, offset[, noAssert])写入64位浮点型数字,大端对齐
buf.writeDoubleLE(value, offset[, noAssert]),写入64位浮点型数字,小端对齐
buf.writeFloatBE(value, offset[, noAssert]),写入32位浮点型数字,大端对齐
buf.writeFloatLE(value, offset[, noAssert]),写入32位浮点型数字,小端对齐
buf.writeInt8(value, offset[, noAssert]),写入有符号8位整型数字
buf.writeInt16BE(value, offset[, noAssert]),写入有符号16位整型数字,大端对齐
buf.writeInt16LE(value, offset[, noAssert]),写入有符号16位整型数字,小端对齐
buf.writeInt32BE(value, offset[, noAssert]),写入有符号32位整型数字,大端对齐
buf.writeInt32LE(value, offset[, noAssert]),写入有符号32位整型数字,小端对齐
buf.writeIntBE(value, offset, byteLength[, noAssert]),以下便不再累述
buf.writeIntLE(value, offset, byteLength[, noAssert])
buf.writeUInt8(value, offset[, noAssert])
buf.writeUInt16BE(value, offset[, noAssert])
buf.writeUInt16LE(value, offset[, noAssert])
buf.writeUInt32BE(value, offset[, noAssert])
buf.writeUInt32LE(value, offset[, noAssert])
buf.writeUIntBE(value, offset, byteLength[, noAssert])
buf.writeUIntLE(value, offset, byteLength[, noAssert])
buffer写操作通过read开头的写api来完成,主要有以下这些:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
buf.readDoubleBE(offset[, noAssert])
buf.readDoubleLE(offset[, noAssert])
buf.readFloatBE(offset[, noAssert])
buf.readFloatLE(offset[, noAssert])
buf.readInt8(offset[, noAssert])
buf.readInt16BE(offset[, noAssert])
buf.readInt16LE(offset[, noAssert])
buf.readInt32BE(offset[, noAssert])
buf.readInt32LE(offset[, noAssert])
buf.readIntBE(offset, byteLength[, noAssert])
buf.readIntLE(offset, byteLength[, noAssert])
buf.readUInt8(offset[, noAssert])
buf.readUInt16BE(offset[, noAssert])
buf.readUInt16LE(offset[, noAssert])
buf.readUInt32BE(offset[, noAssert])
buf.readUInt32LE(offset[, noAssert])
buf.readUIntBE(offset, byteLength[, noAssert])
buf.readUIntLE(offset, byteLength[, noAssert])

使用如下所示,以32无符号整型为例:

1
2
3
4
5
const buf = Buffer.allocUnsafe(8);
buf.writeUInt32BE(0x12345678,0);
console.log(buf);//-><Buffer 12 34 56 78 51 00 00 00>
const data = buf.readUInt32BE(0);
console.log(data.toString(16));//->12345678