1. 程式人生 > 實用技巧 >NodeJS模組Buffer

NodeJS模組Buffer

Buffer 作為 nodejs中重要的概念和功能,為開發者提供了操作二進位制的能力。本文記錄了幾個問題,來加深對 Buffer 的理解和使用:

  • 認識緩衝器
  • 如何申請堆外記憶體
  • 如何計算位元組長度
  • 如何計算位元組長度
  • 如何轉換字元編碼
  • 理解共享記憶體與拷貝記憶體

認識 Buffer(緩衝器)

Buffer 是 nodejs核心 API,它提供我們處理二進位制資料流的功能。Buffer 的使用和 ES2017 的 Uint8Array 非常相似,但由於 node 的特性,專門提供了更深入的 api。

Uint8Array 的字面意思就是:8 位無符號整型陣列。一個位元組是 8bit,而位元組的表示也是由兩個 16 進位制(4bit)的數字組成的。

const buf = Buffer.alloc(1);
console.log(buf); // output: <Buffer 00>

如何申請堆外記憶體

Buffer 可以跳出 nodejs 對堆內記憶體大小的限制。nodejs12 提供了 4 種 api 來申請堆外記憶體:

  • Buffer.from()
  • Buffer.alloc(size[, fill[, encoding]])
  • Buffer.allocUnsafe(size)
  • Buffer.allocUnsafeSlow(size)

Buffer.alloc vs Buffer.allocUnsafe

在申請記憶體時,可能這片記憶體之前儲存過其他資料。如果不清除原資料,那麼會有資料洩漏的安全風險;如果清除原資料,速度上會慢一些。具體用哪種方式,根據實際情況定。

  • Buffer.alloc:申請指定大小的記憶體,並且清除原資料,預設填充 0
  • Buffer.allocUnsafe:申請指定大小記憶體,但不清除原資料,速度更快

根據提供的 api,可以手動實現一個alloc

function pollifyAlloc(size, fill = 0, encoding = "utf8") {
    const buf = Buffer.allocUnsafe(size);
    buf.fill(fill, 0, size, encoding);
    return buf;
}

Buffer.allocUnsafe vs Buffer.allocUnsafeSlow

從命名上可以直接看出效果,Buffer.allocUnsafeSlow更慢。因為當使用Buffer.allocUnsafe建立新的 Buffer 例項時,如果要分配的記憶體小於 4KB,則會從一個預分配的 Buffer 切割出來。 這可以避免垃圾回收機制因建立太多獨立的 Buffer 而過度使用。

這種方式通過消除跟蹤和清理的需要來改進效能和記憶體使用。

如何計算位元組長度

利用 Buffer,可以獲得資料的真實所佔位元組。例如一個漢字,它的字元長度是 1。但由於是 utf8 編碼的漢字,所以佔用 3 個位元組。

直接利用Buffer.byteLength()可以獲得字串指定編碼的位元組長度:

const str = "本文原文地址: xxoo521.com";

console.log(Buffer.byteLength(str, "utf8")); // output: 31
console.log(str.length); // output: 19

也可以直接訪問 Buffer 例項的 length 屬性(不推薦):

console.log(Buffer.from(str,"utf8").length);// output: 31

如何轉換字元編碼

Nodejs 當前支援的編碼格式有:ascii、utf8、utf16le、ucs2、base64、latin1、binary、hex。其他編碼需要藉助三方庫來完成。

下面,是用Buffer.from()和buf.toString()來封裝的 nodejs 平臺的編碼轉換函式:

function trans(str, from = "utf8", to = "utf8") {
    const buf = Buffer.from(str, from);
    return buf.toString(to);
}

// output: 5Y6f5paH5Zyw5Z2AOiB4eG9vNTIxLmNvbQ==
console.log(trans("原文地址: xxoo521.com", "utf8", "base64"));

佛山vi設計https://www.houdianzi.com/fsvi/ 豌豆資源搜尋大全https://55wd.com

共享記憶體與拷貝記憶體

在生成 Buffer 例項,操作二進位制資料的時候,千萬要注意介面是基於共享記憶體,還是基於拷貝底層記憶體。

例如對於生成 Buffer 例項的from(),不同型別的引數,nodejs 底層的行為是不同的。

為了更形象地解釋,請看下面兩段程式碼。

程式碼1

const buf1 = Buffer.from("buffer");
const buf2 = Buffer.from(buf1); // 拷貝引數中buffer的資料到新的例項
buf1[0]++;

console.log(buf1.toString()); // output: cuffer
console.log(buf2.toString()); // output: buffer

程式碼 2

const arr = new Uint8Array(1);
arr[0] = 97;

const buf1 = Buffer.from(arr.buffer);
console.log(buf1.toString()); // output: a

arr[0] = 98;
console.log(buf1.toString()); // output: b

在第二段程式碼中,傳入Buffer.from的引數型別是arrayBuffer。因此Buffer.from僅僅是建立檢視,而不是拷貝底層記憶體。buf1 和 arr 的記憶體是共享的。

在操作 Buffer 的過程中,需要特別注意共享和拷貝的區別,發生錯誤比較難排查。