11-二进制数据-buffer

141 阅读7分钟

二进制数据-buffer

// 模块概览
// Buffer是node的核心模块,开发者利用他来处理二进制数据,比如文件流的读写,网络请求数据的处理
// Buffer的API很多,仅挑选常用的API来进行讲解

创建

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

new Buffer(array)

const buf = new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]);
const array = "buffer".split("").map((v) => {
  return "0x" + v.charCodeAt(0).toString(16);
});
console.log(array.join()); // 0x62,0x75,0x66,0x66,0x65,0x72

Buffer.alloc(length) 创建一个指定大小的 buffer

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

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

const buf3 = Buffer.allocUnsafe(10); // 长度为10的buffer,初始值不确定
console.log(buf3); // <Buffer 00 00 00 00 00 00 00 00 00 00>

const buf4 = Buffer.from([1, 2, 3]); // 长度为3的buffer,初始值为 0x01, 0x02, 0x03
console.log(buf4); // <Buffer 01 02 03>

Buffer.from() 从字符串或者数组创建一个 buffer

Buffer.from(array)

// [0x62, 0x75, 0x66, 0x66, 0x65, 0x72] 为字符串 "buffer"
// 0x62 为16进制,转为十进制就是98,代表的字母就是b
const buf = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]);
console.log(buf.toString()); // buffer

Buffer.from(string, encoding)

const buf = Buffer.from("this is a tést"); //默认采用utf8
console.log(buf.toString()); // this is a tést   因为编码为utf8 所以可以正常打印
console.log(buf.toString("ascii")); // this is a tC)st 转换为字符串时候,编码不是utf8 所以乱码

// 对乱码的分析
const letter = "é";
const buff = Buffer.from(letter); // 默认编码是utf8 这里占据了两个字节 <Buffer c3 a9>
const len = buff.length; // 2
const code = buff[0]; // 第一个字节是 0xc3 即195 超出ascii的最大支持范围
const binary = code.toString(2); // 192的二进制 10101001
const finalBinary = binary.slice(1); // 将高位的1舍弃,变成0101001
const finalCode = parseInt(finalBinary, 2); // 0101001 对应的十进制 67
const finalLetter = String.fromCharCode(finalCode); // 67 对应的字符为 C
console.log(finalLetter); // C

Buffer.from(buffer)

// 创建buffer实例,并将buffer的数据拷贝到新的实例字中去.
const buff = Buffer.from("buffer");
const 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

buffer 比较

buf.equals(otherBuffer)

// 判断两个buffer实例存储的数据是否相同,如果是返回true,否则返回false

// 例子1 编码一样 内容相同
const buf1 = Buffer.from("A");
const buf2 = Buffer.from("A");
console.log(buf1.equals(buf2)); // true

// 例子2 编码一样 内容不容
const buf3 = Buffer.from("A");
const buf4 = Buffer.from("B");
console.log(buf3.equals(buf4)); // false

// 例子3 编码不一样 内容一样
const buf5 = Buffer.from("ABC"); // <Buffer 41 42 43>
const buf6 = Buffer.from("414243", "hex");
console.log(buf5.equals(buf6)); // true

// 只要比较的两者内容相同,buf1.equals(buf2) 就返回true

buf.compare(target, targetStart, targetEnd, sourceStart, sourceEnd)

// 同样是对比两个buffer实例进行比较,不同的是
// 1.可以指定特定的范围(通过start end 指定)
// 2.返回值为整数,达标buf target的大小关系

// 假设返回值为
// 0 buf target 大小相同
// 1 buf 大于 target 也就是说buf应该排在target的后面
// 2 buf 小于 target 也就是说buf应该排在target的前面

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

console.log(buf1.compare(buf1)); // 0
console.log(buf1.compare(buf2)); // -1
console.log(buf1.compare(buf3)); // -1
console.log(buf2.compare(buf1)); // 1
console.log(buf2.compare(buf3)); // 1

console.log([buf1, buf2, buf3].sort(Buffer.compare)); // [ <Buffer 41 42 43>, <Buffer 41 42 43 44>, <Buffer 42 43 44> ]

Buffer.compare(buf1, buf2)

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

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

Buffer.from(Array)

// 这里稍微研究下 Buffer.from(Array) 下面是官网文档对弈API的说明,也就是说,每个array的元素对应一个字节,取值从0到255

数组元素为数字

const buf = Buffer.from([62]);
console.log(buf); // <Buffer 3e>
console.log(buf[0] === parseInt("3e", 16)); // true
console.log(buf[0] === 62); // true

const buf1 = Buffer.from([062]);
console.log(buf1); // <Buffer 3e>
console.log(buf1[0] === 50); // true
console.log(buf1[0] === parseInt(62, 8)); // true
console.log(buf1[0] === parseInt(32, 16)); // true

const buf2 = Buffer.from([0x62]); // <Buffer 62>
console.log(buf2);
console.log(buf2[0] === 98); // true
console.log(buf2[0] === parseInt(62, 16)); // true

数组元素为字符串

// 传入元素为字符串的场景
// 1. 0 开头的字符串,在 parseInt('062')时可以解释为62,也可以解释为50(八进制),这里看到采用第一种解释
// 2. 字符串场景, 跟 parseInt() 有没有关系,暂时不深入了解

const buff = Buffer.from(["62"]);
console.log(buff); // <Buffer 3e>
console.log(buff[0] === parseInt("3e", 16)); // true
console.log(buff[0] === parseInt("62")); // true

const buff2 = Buffer.from(["062"]);
console.log(buff2); // <Buffer 3e>
console.log(buff2[0] === parseInt("3e", 16)); // true
console.log(buff[0] === parseInt("62")); // true

const buff3 = Buffer.from(["0x62"]);
console.log(buff3); //<Buffer 62>
console.log(buff3[0] === parseInt("62", 16)); // true
console.log(buff3[0] === parseInt("0x62")); // true

数组元素大小超出一个字节

const buff = Buffer.from([256]);
console.log(buff); // <Buffer 00>

Buffer.from('1')

// 将 Buffer.from('1')[0] 跟 1 划等号, 其实1对应的编码是49

const buff = Buffer.from("1");
console.log(buff); // <Buffer 31>
console.log(buff[0] === 1); // false

// 对比后发现,编码为1的是个控制字符, 标识 Start of Heading
console.log(String.fromCharCode(49)); // 1
console.log(String.fromCharCode(1)); // '\u0001'

Buffer.concat()

buffer 连接: Buffer.concat(list, totalLength)

// 个人感觉 totalLength 参数很多余,从官方文档上看,是处于性能提升的角度考虑,不过内部实现也只是遍历了list,将length累加得到totalLength,从这点来看,性能优化是几乎可以忽略不计的

const buff1 = Buffer.alloc(10);
const buff2 = Buffer.alloc(20);
const totalLength = buff1.length + buff2.length;
console.log(totalLength); // 30
const buff3 = Buffer.concat([buff1, buff2], totalLength);
console.log(buff3.length); // 30

// 除了上面提到的优化,totalLength还有两点需要注意,假设list里面所有的buffer长度累加和为length
// 1. totalLength>length 返回长度未totalLength的Buffer实例,超出长度的部分填充为0
// 2. totalLength<length 返回长度未 totalLength 的buffer实例,后面部分舍弃

const buff4 = Buffer.from([1, 2]);
const buff5 = Buffer.from([3, 4]);
const buff6 = Buffer.concat([buff4, buff5], 5);
console.log(buff6); // <Buffer 01 02 03 04 00>
console.log(buff6.length); // 5

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

Buffer.copy()

Buffer.copy(target, targetStart, sourceStart, sourceEnd) 拷贝

// 使用比较简单,如果忽略后面的三个参数,那么就是将buf的数据拷贝到target里去,

const buff1 = Buffer.from([1, 2]);
const buff2 = Buffer.alloc(2);
console.log(buff1); // <Buffer 01 02>
console.log(buff2); // <Buffer 00 00>

buff1.copy(buff2);
console.log(buff2); // <Buffer 01 02>

// 另外三个参数
let buff3 = Buffer.allocUnsafe(26);
let buff4 = Buffer.allocUnsafe(26).fill("!");
for (let index = 0; index < 26; index++) {
  buff3[index] = index + 97;
}
console.log(buff3); // <Buffer 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a>
buff3.copy(buff4, 8, 16, 20);
console.log(buff4); // <Buffer 21 21 21 21 21 21 21 21 71 72 73 74 21 21 21 21 21 21 21 21 21 21 21 21 21 21>
console.log(buff4.toString("ascii", 0, 25)); // !!!!!!!!qrst!!!!!!!!!!!!!

buffer.indexOf()

buffer.indexOf(value, byteOffset, encoding) 查找

// 跟数组的查找差不多,需要注意的是, value可能是String Buffer Integer
// string 如果是字符串,那么encoding就是器对应的编码,默认是utf-8
// Buffer 如果是buffer实例,那么会将value中的完整数据跟buf进行对比
// Integer 如果是数字,那么value会被当做无符号的8位整数,取值范围是0到255

const buf = Buffer.from("this is a buffer");
console.log(buf.indexOf("this")); // 0
console.log(buf.indexOf("is")); // 2
console.log(buf.indexOf(Buffer.from("a buffer"))); // 8
console.log(buf.indexOf(97)); // 8
console.log(buf.indexOf(Buffer.from("a buffer example"))); // -1

console.log(Buffer.from("a buffer example").slice(0, 8)); // <Buffer 61 20 62 75 66 66 65 72> 等于 a buffer
console.log(buf.indexOf(Buffer.from("a buffer example").slice(0, 8))); // 8

const utf16Buffer = Buffer.from("\u039a\u0391\u03a3\u03a3\u0395", "ucs2");
console.log(utf16Buffer); // <Buffer 9a 03 91 03 a3 03 a3 03 95 03>
console.log(utf16Buffer.indexOf("\u03a3", 0, "ucs-2")); // 4
console.log(utf16Buffer.indexOf("\u03a3", -4, "ucs-2")); // 6

buf.write()

buf.write(string,offset,length,encoding)

// 将string写入buf实例,同时返回写入的字节数
// 参数如下
// 1. string 写入的字符串
// 2. offset 从buf的第几位开始写入,默认是0
// 3. length 写入多少个字节,默认是buf.length - offset
// 4. encoding 字符串的编码,默认是utf-8

const buff = Buffer.alloc(4);
console.log(buff); // <Buffer 00 00 00 00>
buff.write("ab");
console.log(buff); // <Buffer 61 62 00 00>

buf.fill()

// 用value来填充buf,常用于初始化buf,参数如下
// 1. value 同来填充的内容,可以是 Buffer String, Integer
// 2. offset 从第几位开始填充,默认是0
// 3. end 停止填充的位置 默认是 buf.length
// 4. encoding 如果value是string 那么value 的编码默认为utf-8

const buff = Buffer.alloc(20).fill("a");
console.log(buff.toString()); // aaaaaaaaaaaaaaaaaaaa

const buff1 = Buffer.alloc(20).fill("a", 10);
console.log(buff1.toString()); // aaaaaaaaaa

const buff2 = Buffer.alloc(20).fill("a", 10, 15);
console.log(buff2.toString()); // aaaaa

buf.toString()

buf.toString

const buff = Buffer.from("hello");
console.log(buff.toString()); // hello
console.log(buff.toString("utf-8", 1)); // ello
console.log(buff.toString("utf-8", 1, 2)); // e

buf.toJSON()

buf.toJSON() 转成 json 字符串

const buff = Buffer.from("hello");
console.log(buff.toJSON()); // { type: 'Buffer', data: [ 104, 101, 108, 108, 111 ] }

buf.values(), buf.keys(), buf.entries()

buf.values(), buf.keys(), buf.entries() 遍历

// 用于对buf进行 for of 的遍历

const buff = Buffer.from("abcde");
for (const key of buff.keys()) {
  console.log("key is", key);
  // key is 0
  // key is 1
  // key is 2
  // key is 3
  // key is 4
}

for (const value of buff.values()) {
  console.log("value is", value);
  // value is 97
  // value is 98
  // value is 99
  // value is 100
  // value is 101
}

for (const pair of buff.entries()) {
  console.log("pair is", pair, pair[0], pair[1]);
  // pair is [ 0, 97 ] 0 97
  // pair is [ 1, 98 ] 1 98
  // pair is [ 2, 99 ] 2 99
  // pair is [ 3, 100 ] 3 100
  // pair is [ 4, 101 ] 4 101
}

buf.slice()

buf.slice(start, end) 截取

// 用于截取buf,并返回一个新的buffer实例,需要注意的是,这里返回的buffer实例指向仍然是buf的内存地址,所以对新的buffer实例的修改会影响到buf

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

let buff2 = buff1.slice();
console.log(buff2); // <Buffer 61 62 63 64 65>

let buff3 = buff1.slice(1, 3);
console.log(buff3); // <Buffer 62 63>
console.log(buff1); // <Buffer 61 62 63 64 65>

buff3[0] = 97;
console.log(buff1); // <Buffer 61 61 63 64 65>

TODO

// 1. 创建、拷贝、截取、转换、查找
// 2. buffer、arraybuffer、dataview、typedarray
// 3. buffer vs 编码
// 4. Buffer.from()、Buffer.alloc()、Buffer.alocUnsafe()
// 5. Buffer vs TypedArray