8000 基础模块 之 Buffer 模块 · Issue #36 · fengshi123/blog · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

基础模块 之 Buffer 模块 #36

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
fengshi123 opened this issue Dec 27, 2021 · 0 comments
Open

基础模块 之 Buffer 模块 #36

fengshi123 opened this issue Dec 27, 2021 · 0 comments

Comments

@fengshi123
Copy link
Owner

前言

在前端,我们只需做字符串级别的操作,很少接触字节、进制等底层操作,一方面这足以满足日常需求,另一方面 在 ECMAScript 2015 (ES6) 引入 TypedArray 之前,JavaScript 语言没有读取或操作二进制数据流的机制;然而在后端,处理文件、网络协议、图片、视频等是非常常见的,尤其像文件、网络流等操作处理的都是二进制数据。为了让 javascript 能够处理二进制数据,node 封装了一个 Buffer 类,主要用于操作字节,处理二进制数据。

一个 Buffer 类似于一个整数数组,可以取下标,有 length 属性,有剪切复制操作等,很多 API 也类似数组,但 Buffer 的大小在被创建时确定,且无法调整。Buffer 处理的是字节,两位十六进制,因此在整数范围就是0~255。

Buffer 可以与string 互相转化,还可以设置字符集编码。Buffer 用来处理文件 I/O、网络 I/O 传输的二进制数据,string 用来呈现。在处理文件 I/O、网络 I/O 传输的二进制数据时,应该尽量以 Buffer 形式直接传输,速度会得到很好的提升。

Buffer 内存分配与性能优化:Buffer 是一个典型的 javascript 与 C++ 结合的模块,与性能有关的用 C++ 来实现,javascript 负责衔接和提供接口。Buffer 所占的内存不是 V8 分配的,是独立于 V8 堆内存之外的内存,通过 C++ 层面实现内存申请、javascript 分配内存。值得一提的是,每当我们使用 Buffer.alloc(size) 请求一个 Buffer 内存时,Buffer 会以 8KB 为界限来判断分配的是大对象还是小对象,小对象存入剩余内存池,不够再申请一个 8KB 的内存池;大对象直接采用 C++ 层面申请的内存。因此,对于一个大尺寸对象,申请一个大内存比申请众多小内存池快很多。
注意点:
1、Buffer 对象类似于数组,它的元素为 16 进制的两位数,即 0 到 255 的数值;不同编码的字符串占用的元素个数各不相同,例如在 UTF-8 编码下:中文占用 3 个字符,字母和半角标点符号占用 1 个字符;
2、如果给 Buffer 元素赋值如果小于 0,就将该值逐次加 256,直到得到一个 0 到 255 之间的整数;如果得到的数值大于 255,就逐次减 256,直到得到 0-255 区间的数值;如果是小数,则舍弃小数部分,只保留整数部分;

1、创建

  • new Buffer(array)
  • Buffer.alloc(length)
  • Buffer.allocUnsafe(length)
  • Buffer.from(array)

1.1、new Buffer

注意这个用法存在安全问题,已被废弃;

const buf1 = new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]);
console.log(buf1.toString());  // buffer

1.2、Buffer.alloc

const buf2 = Buffer.alloc(10); // 长度为 10 的 buffer,初始值为 0x0?
console.log(buf2); // <Buffer 00 00 00 00 00 00 00 00 00 00>

const buf3 = Buffer.alloc(10, 1); // 长度为 10 的 buffer,初始值为 0x1?
console.log(buf3); // <Buffer 01 01 01 01 01 01 01 01 01 01>

1.3、Buffer.from 用法

1.3.1、Buffer.from(array)

const buf4 = Buffer.from([1, 2, 3]) 
console.log(buf4.toString()); // <Buffer 01 02 03>

const buf5 = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]);
console.log(buf5);  // <Buffer 62 75 66 66 65 72>

1.3.2、Buffer.from(string[, encoding])

默认编码 utf8

const buf6 = Buffer.from('buffer');
console.log(buf6);  // <Buffer 62 75 66 66 65 72>
console.log(buf6.toString()); // buffer

1.3.3、Buffer.from(buffer)

创建新的 Buffer 实例,并将 buffer 的数据拷贝到新的实例中去

let buff = Buffer.from('buffer');
let buff2 = Buffer.from(buff);

console.log(buff.toString());  // 输出:buffer
console.log(buff2.toString());  // 输出:buffer

buff2[0] = 0x61;

console.log(buff.toString());  // 输出:buffer
console.log(buff2.toString());  // 输出:auffer

2、比较

2.1、buf.equals(otherBuffer)

// 例子一:编码一样,内容相同 => true
var buf1 = Buffer.from('A');
var buf2 = Buffer.from('A');
console.log( buf1.equals(buf2) );  // true

// 例子二:编码一样,内容不同  => false
var buf3 = Buffer.from('A');
var buf4 = Buffer.from('B');
console.log( buf3.equals(buf4) );  // false

// 例子三:编码不一样,内容相同  => false
var buf5 = Buffer.from('ABC');  
var buf6 = Buffer.from('ABC', 'hex');
console.log(buf5.equals(buf6));  // false

2.2、buf.compare(target[, targetStart[, targetEnd[, sourceStart[, sourceEnd]]]])

const buf1 = Buffer.from('ABC');
const buf2 = Buffer.from('BCD');
const buf3 = Buffer.from('ABCD');
const buf4 = Buffer.from('ABC');

//  0
console.log(buf1.compare(buf4));
// -1
console.log(buf1.compare(buf2));
// -1
console.log(buf1.compare(buf3));
// 1
console.log(buf2.compare(buf3));
// 0
console.log(buf2.compare(buf3, 1, 3, 0, 2));

2.3、Buffer.compare(buf1, buf2)

跟 buf.compare(target) 大同小异,一般用于排序

const buf1 = Buffer.from('1234');
const buf2 = Buffer.from('0123');
const arr = [buf1, buf2];
// [ <Buffer 30 31 32 33>, <Buffer 31 32 33 34> ]
console.log(arr.sort(Buffer.compare));

3、其它

3.1、连接

Buffer.concat(list[, totalLength])
其中 totalLength 有两点需要注意。假设 list 里面所有 buffer 的长度累加和为 length

  • totalLength > length:返回长度为 totalLength 的 Buffer 实例,超出长度的部分填充 0;
  • totalLength < length:返回长度为 totalLength 的 Buffer 实例,后面部分舍弃;
const buff1 = Buffer.from([1, 2]);
const buff2 = Buffer.from([3, 4]);

const length = buff1.length + buff2.length;
const buff3 = Buffer.concat([buff1, buff2], length);
console.log(buff3.length); // 4
console.log(buff3); // <Buffer 01 02 03 04>


const buff4 = Buffer.concat([buff1, buff2], 3);
console.log(buff4.length);  // 3
console.log(buff4);  // <Buffer 01 02 03>

const buff5 = Buffer.concat([buff1, buff2], 5);
console.log(buff5.length);  // 5
console.log(buff5);  // <Buffer 01 02 03 04 00>

3.2、拷贝

buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])

var buff1 = Buffer.from([1, 2]);
var buff2 = Buffer.from([3, 4, 5]);

// 1、不传后面 3 个参数的情况
buff1.copy(buff2);
console.log(buff2);  // <Buffer 01 02 05>

// 2、传后面 3 个参数的情况
buff1.copy(buff2, 1, 0, buff1.length);
console.log(buff2);  // <Buffer 03 01 02>

3.3、查找

buf.indexOf(value[, byteOffset][, encoding])
跟数组的查找差不多,需要注意的是,value 可能是 String、Buffer、Integer中的任意类型。

  • String:如果是字符串,那么 encoding 就是其对应的编码,默认是 utf8。
  • Buffer:如果是 Buffer 实例,那么会将 value 中的完整数据,跟 buf 进行对比。
  • Integer:如果是数字,那么 value 会被当做无符号的 8 位整数,取值范围是 0 到 255。

另外,可以通过 byteOffset 来指定起始查找位置;

const buf = Buffer.from('this is a buffer');
console.log(buf.indexOf('this')); // 0
console.log(buf.indexOf(Buffer.from('a buffer'))); // 8
console.log(buf.indexOf(Buffer.from('a buffer example'))); // -1
// (97 is the decimal ASCII value for 'a')
console.log(buf.indexOf(97)); // 8

console.log(buf.indexOf('is', 3)); // 5

3.4、写入

buf.write(string[, offset[, length]][, encoding])
将 sring 写入 buf 实例,同时返回写入的字节数;
参数如下:

  • string:写入的字符串;
  • offset:从 buf 的第几位开始写入,默认是 0;
  • length:写入多少个字节,默认是 buf.length - offset;
  • encoding:字符串的编码,默认是 utf8;
var buff1 = Buffer.alloc(4);
const num1 = buff1.write('a');  // 返回 1
console.log(buff1);  // 打印 <Buffer 61 00 00 00>

var buff2 = Buffer.alloc(4);
const num2 = buff2.write('abc', 1, 2);  // 返回 2
console.log(buff2);  // 打印 <Buffer 00 61 62 00>

3.5、填充

buf.fill(value[, offset[, end]][, encoding])
用 value 填充 buf,常用于初始化 buf。参数说明如下:

  • value:用来填充的内容,可以是 Buffer、String 或 Integer;
  • offset:从第几位开始填充,默认是 0;
  • end:停止填充的位置,默认是 buf.length;
  • encoding:如果 value 是 String,那么为 value 的编码,默认是 utf8;
var buff = Buffer.alloc(10).fill('a', 1, 3);
console.log(buff);  // <Buffer 00 61 61 00 00 00 00 00 00 00>

3.6、转换成字符串

buf.toString([encoding[, start[, end]]])

var buff = Buffer.from('hello');
console.log( buff.toString() );  // hello
console.log( buff.toString('utf8', 0, 2) );  // he

3.7、遍历

buf.values()、buf.keys()、buf.entries()

var buff = Buffer.from('abcde');

for(const key of buff.keys()){
    console.log('key is %d', key);
}

for(const value of buff.values()){
  console.log('value is %d', value);
}

for(const pair of buff.entries()){
  console.log('buff[%d] === %d', pair[0], pair[1]);
}

3.8、截取

buf.slice([start[, end]])
用于截取 buf,并返回一个新的 Buffer 实例。需要注意的是,这里返回的 Buffer 实例,指向的仍然是 buf 的内存地址,所以对新 Buffer 实例的修改,也会影响到 buf。

var buff1 = Buffer.from('abcde');
console.log(buff1);  // <Buffer 61 62 63 64 65>

var buff2 = buff1.slice(1, 3);
console.log(buff2);  // <Buffer 62 63>

buff2[0] = 128; 
console.log(buff2);  //  <Buffer 80 63>
console.log(buff1);  // <Buffer 61 80 63 64 65>

4、参考文献

(1)Nodejs进阶:核心模块Buffer常用API使用总结
(2)认识node核心模块--从Buffer、Stream到fs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant
0