Node-Buffer

前言

默认文件读取操作, 读取出来的都是buffer
内存的表示方式就是Buffer ,内存二进制的 、Buffer十六进制的

1
2
// node.md
你好
1
2
3
4
5
6
let fs = require('fs');
let path = require('path');

let r = fs.readFileSync(path.resolve(__dirname, 'note.md'));
console.log(r);
// <Buffer e4 bd a0 e5 a5 bd>

进制转化

  • 将任意进制转化成任意进制:
    • xxx.toString(‘xxx’); 只是值会变成字符串
    • toString(‘utf8’ || ‘base64’); 不支持gbk
  • 将任意进制转化成十进制
    • parseInt(‘0xff’,16); // 十六进制 => 十进制
  • 示例

0.1 + 0.2 != 0.3

1
2
3
// 我们需要将值存到内存中 保存的时候存储的是二进制
0.0001100110011001100110011001100110011001100110011001101 0.1
0.001100110011001100110011001100110011001100110011001101 0.2
  • 进制转化 小数 *2 取整法
1
2
3
4
5
6
7
8
9
10
// 0.1如何变成二进制 如下:
0.1 * 2 = 0.2 =>0
0.2 * 2 = 0.4 =>0
0.4 * 2 = 0.8 =>0
0.8 * 2 = 1.6 =>1
0.6 * 2 = 1.2 =>1
0.2 * 2 = 0.4 =>0
0.4 * 2 = 0.8 =>0
0.8 * 2 = 1.6 =>1
...循环
  • 整数 进制转化 基于编码
    ASCII(美国) 默认 就一个字节来表示是一个字母或者符号
    1个字节 有8个bit 最大是 8个1 => 10进制 255
    默认 字母 常见符号 \r \n
    gbk 用两个字节来表示中文
    utf-8 用两个字节来表示中文

  • 将10进制 转化成其他进制 0xff 0b 0o

1
2
console.log(100..toString(16)); // 64 值变成了字符串
console.log((100).toString(16)); // 64 值变成了字符串
  • 十六进制转化成十进制
1
2
console.log((0xff).toString(10)); // 255
// 255 string
  • 十六进制转化成十进制
1
2
console.log(parseInt('0xff',16), typeof parseInt('0xff',16));
// 255 'number'
  • base64

二进制的值不能超过64 (核心就是进制的转化)

1
2
3
4
5
6
7
8
9
10
// 在浏览器header中,任意的url中都可以采用base64,前端实现文件预览 (fileReadReader 读出来也是base64)
// 特点:转码后的结果 比原来的内容 大
// 分析:
// 如果一个汉字
console.log(Buffer.from('珠')); // e7 8f a0 <Buffer e7 8f a0>
console.log(0xe7.toString(2));
console.log(0x8f.toString(2));
console.log(0xa0.toString(2));
// 11100111 10001111 10100000 左侧是二进制的格式,现在要把 3*8的格式转成 6*4如下:
// 111001 111000 111110 100000

base64 转化后取特定的字符串取值

1
2
3
4
5
6
7
8
let str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
str+= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.toLowerCase();
str+= '0123456789+/'
let result = str[0b111001] + str[0b111000] + str[0b111110] + str[0b100000];
console.log(result); // 54+g 可以在线转化 就是珠

// base64就是编码转化 不需要发送http请求。大小会比以前大
console.log(Buffer.from('珠').toString('base64')); // 54+g

编码问题

iconv-lite

1
2
3
4
5
6
7
8
9
10
11
12
// 现在新建一个test.md gbk格式的文件
const fs = require('fs');
const path = require('path');
let r = fs.readFileSync(path.resolve(__dirname, 'test.md'), 'utf-8'); // 读取文件只能设置utf8或者不设置默认输出的是buffer
console.log(r); // 可以看到都是乱码
// 这里可以引入第三方的包 iconv-lite gbk 转成 utf-8

const iconv = require('iconv-lite');
const fs = require('fs');
const path = require('path');
let r = fs.readFileSync(path.resolve(__dirname, 'test.md'));
console.log(iconv.decode(r, 'gbk')); // 你好

Buffer

把二进制表现成了10进制 可以和字符串进行转化

1) Buffer的声明方式

Buffer代表的是内存 一旦声明就不能扩展 数组的话可以扩展

  • 固定大小
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let buf = Buffer.alloc(5);// 分配5个  出来的结果很像数组
console.log(buf);
// <Buffer 00 00 00 00 00>

let buf = Buffer.allocUnsafe(5); // 随机抓取5个内存
console.log(buf);
// <Buffer 06 00 00 00 14> 可能包含之前的信息,优点是速度快

let buf = Buffer.allocUnsafe(5);
buf.fill(0); // 手动填充
console.log(buf);
// <Buffer 00 00 00 00 00>
// allocUnsafe + fill => alloc

let buf = Buffer.from([100, 200, 300]); // 数组 少用 手动写
console.log(buf);
// <Buffer 64 c8 2c>

let buf = Buffer.from('加菲'); // 字符串
console.log(buf);
// <Buffer e5 8a a0 e8 8f b2> 这个同样也不能扩容,因为字符串长度是固定的

常用的赋值方式是第一种和最后一种

2) Buffer的常见方法

和数组类似

2.1) slice
1
2
3
4
5
6
7
8
9
10
11
let arr = [[1,2,3],4,5];
let newArr = arr.slice(0); // 浅拷贝
newArr[0][1] = 100;
console.log(arr, newArr);
// [ [ 1, 100, 3 ], 4, 5 ] [ [ 1, 100, 3 ], 4, 5 ]

let buffer = Buffer.from('加菲'); // <Buffer 64 8a a0 e8 8f b2>
let newBuffer = buffer.slice(0);
newBuffer[0] = 100;
console.log(buffer);
//<Buffer e5 8a a0 e8 8f b2>

Buffer存放的都是内存地址,如果截取了某一段,改变的时候也是更改了这个内存地址

2.2)判断Buffer类型
1
console.log(Buffer.isBuffer(buffer)); //true 是不是Buffer
2.3) Buffer不能扩展大小

copy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let buffer1 = Buffer.from("珠");
let buffer2 = Buffer.from("珠");

// 现在想要把这2块内存放到一起
// 可以声明一个大Buffer 将2个放在一起
let buff = Buffer.alloc(6);
let buffer1 = Buffer.from("珠");
let buffer2 = Buffer.from("珠");

// 当前buffer.copy(目标buffer,目标的开始位置,源的开始,源的结束)
buffer1.copy(buff,0,0,3);
buffer2.copy(buff,3,0,3);
console.log(buff, buff.toString())
// <Buffer e7 8f a0 e7 8f a0> '珠珠'
  • 写一下这个copy 的原理
1
2
3
4
5
Buffer.prototype.copy = function(targetBuffer, targetStart, sourceStart = 0, sourceEnd = this.length){
for(let i = 0; i < sourceEnd - sourceStart; i++){ // 确定循环次数
targetBuffer[targetStart + i] = this[ sourceStart + i];
}
}
2.4) concat拼接
1
2
3
4
5
let buffer1 = Buffer.from("珠");
let buffer2 = Buffer.from("珠");
let newBuffer = Buffer.concat([buffer1, buffer2], 10); //可以提供大小 也可以不写
console.log(newBuffer, newBuffer.toString());
// <Buffer e7 8f a0 e7 8f a0 00 00 00 00> '珠珠\u0000\u0000\u0000\u0000'
  • 写下concat的原理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Buffer.concat = function (list, length = list.reduce( (a,b)=>a+b.length,0)){
let buffer = Buffer.alloc(length);
let offset = 0;
list.forEach( b=>{
b.copy(buffer, offset);
offset+= b.length;
})
return buffer;
}
let buffer1 = Buffer.from("珠");
let buffer2 = Buffer.from("珠");
let newBuffer = Buffer.concat([buffer1, buffer2]); //可以提供大小 也可以不写
console.log(newBuffer, newBuffer.toString());
// <Buffer e7 8f a0 e7 8f a0> '珠珠'

isBuffer 、length字节数 、concat 、toString()|toString(‘base64’) 、slice 、fill
Buffer场景:文件读写 、数据读写

3) 扩展Buffer的方法

1
2
3
let buffer = Buffer.from('加菲加菲加菲'); 
console.log(buffer.indexOf('加',2));
// 一个中文字占3个字节,2代表从第二个字节开始 6
  • 行读取器

文本 希望每拿到一行就打印

1
2
3
4
5
6
7
let buffer = Buffer.from(`加菲加菲加菲加菲
加菲加菲加菲加菲加菲
加菲加菲加菲加菲`);
console.log(buffer);
// <Buffer e5 8a a0 e8 8f b2 e5 8a a0 e8 8f b2 e5 8a a0 e8 8f b2 e5 8a a0 e8 8f b2 0a e5 8a a0 e8 8f b2 e5 8a a0 e8 8f b2 e5 8a a0 e8 8f b2 e5 8a a0 e8 8f b2 e5 ... >
console.log(Buffer.from('\n')); // 0a
//console.log(Buffer.from('\r\n')); // 0d 0a window的话是\r\n

现在希望能够通过 buffer.split(‘\n’) 就能把上面的数据分成三段取出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let buffer = Buffer.from(`加菲加菲加菲加菲
加菲加菲加菲加菲加菲
加菲加菲加菲加菲`);
Buffer.prototype.split = function(sep) {
// 因为如果这里sep传进来的是中文字,直接取长度是1,但其实Buffer的长度是3
let len = Buffer.from(sep).length; // 取sep的Buffer的长度, 这是分隔符的长度
let offset = 0;
let result = [];
let current = null;
// 把找到的位置赋给current
while ( (current = this.indexOf(sep, offset)) !== -1) {
result.push(this.slice(offset, current));
offset = current + len; // 增加查找偏移量
}
//如line3没有分隔符了要把最后的取上
result.push(this.slice(offset));
return result;
}
console.log(buffer.split('\n'));
// 执行结果:
[ <Buffer e5 8a a0 e8 8f b2 e5 8a a0 e8 8f b2 e5 8a a0 e8 8f b2 e5 8a a0 e8 8f b2>,
<Buffer e5 8a a0 e8 8f b2 e5 8a a0 e8 8f b2 e5 8a a0 e8 8f b2 e5 8a a0 e8 8f b2 e5 8a a0 e8 8f b2>,
<Buffer e5 8a a0 e8 8f b2 e5 8a a0 e8 8f b2 e5 8a a0 e8 8f b2 e5 8a a0 e8 8f b2> ]