zoukankan      html  css  js  c++  java
  • 学习笔记—Buffer的常用方法与实现

    日常的学习笔记,包括 ES6、Promise、Node.js、Webpack、http 原理、Vue全家桶,后续可能还会继续更新 Typescript、Vue3 和 常见的面试题 等等。


    Buffer

    参考文献 buffer 缓冲区

    缓冲区 Buffer 是暂时存放输入输出数据的一段内存。JS没有二进制数据类型,而在处理TCP和文件流的时候,必须要处理二进制数据。所以 Node 提供了一个 Buffer对象 来提供对二进制数据的操作。

    Buffer 表示固定内存分配的全局对象,也就是说要放到缓存区中的字节数需要提前确定。而 Buffer 好比由一个 8位字节 组成的数组,可以有效的在JavasScript中表示二进制数据。

    Buffer 简单来说就是 node 中的 16进制 ,但 Buffer 在内存的标识也会全部使用 2进制 来进行表示。

    (注:目前以无法使用 new Buffer() 创建 Buffer 实例,会存在安全性等问题。已被禁止使用。)

    Buffer.alloc

    Buffer 代表的是内存,一旦声明好,就不能进行更改。

    如果想要更改 Buffer 的大小,改小则对内存进行 截取 。 改大的话就需要创建一个更大的内存空间,将数据拷贝进行,也就是我们俗称的 扩容

    这时候就可以用到 Buffer 类的内置方法, Buffer.alloc()

    Buffer.alloc(size[, fill[, encoding]]) ,表示分配 size 个字节的新 Buffer。 如果 fillundefined,则 Buffer 将以零填充。

    • size:新的 Buffer 所需的长度。
    • fill:用于预填充新 Buffer 的值,默认值为 0
    • encoding:如果 fill 是字符串,则这就是它的编码。默认值为 utf8
    // 创建了一个指定长度的buffer实例
    let buf1 = Buffer.alloc(3); // 最小单位是 3字节
    console.log(buf1); // <Buffer 00 00 00>
    let buf2 = Buffer.alloc(6); // 单位是 6
    console.log(buf2); // <Buffer 00 00 00 00 00 00>
    

    Buffer.from

    上一篇文章中,我们曾经使用 Buffer.from 来创建过 base64

    Buffer.from 方法用于创建包含指定 字符串数组buffer 的新 Buffer 实例。

    Buffer.from 可以传入的参数有很多,这里我们只扩展 字符串数组 两种。

    Buffer.from(array)

    Buffer.from(array) 使用 0255 范围内的字节 array 分配新的 Buffer。 该范围之外的数组条目将被截断以符合它。

    let buf1 = Buffer.from([0xe8, 0x8e, 0xab])
    console.log(buf1); // <Buffer e8 8e ab>
    let buf2 = Buffer.from([256, 0x8e, 0xab]) // 超过长度会自动取余
    console.log(buf2); // <Buffer 00 8e ab>
    let buf2 = Buffer.from(['aaa', 0x8e, 0xab]) // 不能在数组内存放其他数据类型
    console.log(buf2); // <Buffer 00 8e ab>
    

    (注:很少使用这种方法来定义 buffer ,因为需要指定存放的内容)

    Buffer.from(string)

    Buffer.from(string[, encoding]) 创建包含 string 的新 Bufferencoding 参数标识将 string 转换为字节时要使用的字符编码。

    • string: 要编码的字符串。
    • encodingstring 的编码,默认值为 utf8
    let buf = Buffer.from('莫小尚');
    console.log(buf); // <Buffer e8 8e ab e5 b0 8f e5 b0 9a>
    

    Buffer.from(string) 是目前 Buffer 经常使用的方法。这个方法可以存储数据,存储的数据可以用 Buffer 进行表示。同时也可以和字符串之间进行相互转化。

    // 使用 .toString() 方法,将buffer转换成字符串
    console.log(buf.toString()); // 莫小尚
    // 可以转换成任意指定编码
    console.log(buf.toString('base64')); // 6I6r5bCP5bCa
    

    buffer.toString() 默认值为 utf8

    我们在进行读写操作时,如果不指定编码,则所有读取的文件内容都是 buffer 类型。

    // test.txt
    123456789
    // index.js
    const fs = require('fs');
    let r = fs.readFileSync('./test.txt'); // 不指定 utf-8 编码格式 
    console.log(r); // <Buffer 31 32 33 34 35 36 37 38 39>
    

    Buffer的扩容

    我们在操作 Buffer 时,会遇到原本规定的内存大小不够的情况,这样我们就需要对 Buffer 进行扩容。

    const buf1 = Buffer.from('莫')
    const buf2 = Buffer.from('小尚')
    const bigBuf = Buffer.alloc(buf1.length + buf2.length);
    console.log(bigBuf); // <Buffer 00 00 00 00 00 00 00 00 00>
    

    buf.length 返回 buf 中的字节数。

    这样我们就创建了一个更大的 Buffer 对象,现在我们需要将内容拷贝到这个大 buffer 中。

    buf1.copy(bigBuf, 0, 0, buf1.length);
    buf2.copy(bigBuf, buf1.length, 0, buf2.length);
    console.log(bigBuf.toString()); // 莫小尚
    

    这里我们使用到了 buf.copy() 方法,稍后会进行讲解。

    这样我们就完成了一个简单的扩容操作。 (注:在实际工作中,此方法并不常用。我们一般会使用 buf.concat 来进行扩容操作。)

    const buf1 = Buffer.from('莫');
    const buf2 = Buffer.from('小尚');
    const bigBuf = Buffer.concat([buf1, buf2]);
    console.log(bigBuf.toString()); // 莫小尚
    

    关于 buf.concat 的使用,稍后也会进行详解。

    Buffer.copy

    buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]]) 将数据从 buf 的区域复制到 target 的区域,即使 target 内存区域与 buf 重叠。

    • target:被拷贝的 BufferUint8Array,也就是我们的 大容量 Buffer
    • targetStarttarget 内开始写入的偏移量,默认值为 0
    • sourceStartbuf 内开始复制的偏移量,默认值为 0
    • sourceEndbuf 内停止复制的偏移量(不包括),默认值为 buf.length
    • [callBack]:复制的字节数。

    我们刚才使用了 buf.copy 进行了简单的扩容操作,那么 buf.copy 的实现原理是什么呢。

    方法实现

    首先我们清楚, buf.copy 中一共接受四个参数,分别是 targettargetStartsourceStartsourceEnd

    现在我们来看一下完成后的代码。

    Buffer.prototype.copy = function (target, targetStart, sourceStart = 0, sourceEnd = this.length) {
    	for (let i = sourceStart; i < sourceEnd; i++) {
    		target[targetStart++] = this[i];
    	}
    }
    

    实现了 buf.copy ,我们就可以来看一下 buf.concat 方法了。

    Buffer.concat

    Buffer.concat(list[, totalLength]) 会返回新的 Buffer,它是将 list 中的所有 Buffer 实例连接在一起的结果。

    • list:要拼接的 BufferUint8Array 实例的数组列表。
    • totalLength:连接时 listBuffer 实例的总长度。
    • callBack:返回一个新的 Buffer

    在上面的例子中,我们使用 buf.concat 实现了一个 Buffer 扩容的例子。

    下面我们就来详解一下它的方法实现。

    方法实现

    根据 buf.concat 的使用方式,我们可以大概了解到一个思路。 那就是将传入的 Buffer 实例通过 拷贝 的方式将其拼接成一个大的 Buffer 类。

    这样我们就可以大概手写出其实现原理了。

    Buffer.concat = function (bufferList, len = bufferList.reduce((a, b) => a + b.length, 0)) {
    	let buffer = Buffer.alloc(len);
    	// 记录下一次 开始拼接的 位置
    	let offset = 0;
    	bufferList.forEach(buf => {
        // 判断是不是 Buffer
        if (Buffer.isBuffer(buf)) {
    			buf.copy(buffer, offset);
    			offset += buf.length;
    		}
    	})
    	return buffer;
    }
    

    这样我们就完成了 buf.concat 的实现。

    本篇文章由 莫小尚 创作,文章中如有任何问题和纰漏,欢迎您的指正与交流。
    您也可以关注我的 个人站点博客园掘金,我会在文章产出后同步上传到这些平台上。
    最后感谢您的支持!

  • 相关阅读:
    剑指offer2:替换空格
    题目:求第五个人的年龄
    编写一个函数,输入n为偶数时,调用函数求1/2+1/4+…+1/n,当输入n为奇数时,调用函数1/1+1/3+…+1/n(利用指针函数)
    原创| 输入数组,最大的与第一个元素交换,最小的与最后一个元素交换,输出数组。
    编程题30 题目:将一个数组逆序输出。
    编程题32 题目:取一个整数a从右端开始的4~7位。
    编程题31 题目:将一个数组逆序输出。
    daisy的词源
    2018年学习日志
    11
  • 原文地址:https://www.cnblogs.com/moxiaoshang/p/15743391.html
Copyright © 2011-2022 走看看