學習筆記—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 的實現。

本篇文章由 莫小尚 創作,文章中如有任何問題和紕漏,歡迎您的指正與交流。
您也可以關注我的 個人站點博客園掘金,我會在文章產出后同步上傳到這些平台上。
最后感謝您的支持!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM