node 核心模塊學習之 Buffer

node 核心模塊學習之Buffer

何爲 Buffer

在ES6引入 TypeArray 以前,JS沒有能讀取和操做二進制數據流的機制,Buffer 做爲 Node API 引入,以便能和 TCP 網絡流、文件流等進行交互。 目前 ES6 中有 TypeArray 了,Buffer 類以更加優化和適用的於 Node 操做的方式實現了 Unit8Array API。
總之,Buffer 就是用來操做二進制數據的,位於全局變量中,無需引入便可使用。
Buffer 實例相似於 整型數組,緩衝區大小在建立是肯定,不能調整,內存有C++申請,JS 分配。html

Instances of the Buffer class are similar to arrays of integers but correspond to fixed-sized, raw memory allocations outside the V8 heap. The size of the Buffer is established when it is created and cannot be changed.

...mechanism for reading or manipulating streams of binary data. The Buffer class was introduced as part of the Node.js API to make it possible to interact with octet streams in the context of things like TCP streams and file system operations。node

Buffer 緩存區,計算機讀取速度和處理不匹配,讀取速度高於處理速度時,會開闢一段內存區域存放待處理的數據,這個段內存就叫緩衝區json

實例化 Buffer

在v6.0以前建立Buffer對象直接使用new Buffer()構造函數來建立對象實例,可是Buffer對內存的權限操做相比很大,能夠直接捕獲一些敏感信息,存在安全隱患,以後的版本,用下面幾個函數實例畫一個Buffer:數組

  • Buffer.from()
  • Buffer.alloc()
  • Buffer.allocUnsafe()
函數 參數 返回值
from arry 包含array的字節副本的Buffer,數組中的每一項表示一個8位字節的數字,故值在0--255之內,不然取餘
from buffer 從buffer複製一個新的buffer
from arrayBuffer[,byteOffet,[,length]] 與arrayBuffer共享內存的Buffer
from string[,encoding] string初始化的Buffer
alloc size[,fill[encoding]] 指定大小的Buffer實例額,省略 fill,默認用0填充
allocUnsafe size 指定大小的buffer,不被初始化,可能包含敏感信息

allocUnsafe分配的內存不被初始化,即歸零,內存速度快,可是可能包含舊數據,不覆蓋這些數據,就可能形成內存泄漏。緩存

編碼

支持如下編碼:安全

  • utf
  • ascii
  • base64
  • binary
  • utf16le
  • hex

讀寫緩衝區

buffer.write(string[offset,[length]][,encoding])markdown

  • string - 寫入緩衝區的字符串;
  • offset - 開始寫入的位置,默認 0;
  • length - 寫入字節,默認 buf.length
  • encoding - 編碼,默認 utf8。

返回值:int:寫入的實際大小,沒有足夠的空間保存,只會寫入一部分。
buffer.toString([endcoding[,start=0[,end=buffer.length]]]) 解碼指定緩衝區的數據,並按 endcoding 編碼格式返回字符串。網絡

let buf = Buffer.alloc(10);//分配 10 個字節的空間
console.log(buf)//<Buffer 00 00 00 00 00 00 00 00 00 00> 沒 fill ,用 0 填充

let len = buf.write('this is a buffer');// 16個字節
console.log(buf)//<Buffer 74 68 69 73 20 69 73 20 61 20>
console.log(len)//10 

// 上面的代碼和下面的同樣
let buffer = Buffer.from('this is a buffer'.substring(0, 10))
console.log(buffer)//<Buffer 74 68 69 73 20 69 73 20 61 20>
console.log(buffer.length)//10

let buf2 = Buffer.alloc(8,10);// 分配 8 個字節的內存,用 10 填充
console.log(buf2)//<Buffer 0a 0a 0a 0a 0a 0a 0a 0a>
let size = buf2.write('this a buffer', 2, 2);//從索引 2 開始寫,寫入2字節的數據
console.log(buf2)//<Buffer 00 00 74 68 00 00 00 00>
console.log(size)//2
console.log(buf.toString('utf16le',2,8));//獩槧⁳
console.log(buf.toString('base64',2,8));//aXMgaXMg
console.log(buf.toString('ascii',2,8));//is is
console.log(buf.toString('utf8',2,8));//is is
console.log(buf.toString('hex',2,8));//697320697320

填充 buffer.fill(value[,offset=0[,end=buffer.length]][,endcoding])

value 能夠是 BufferStringIntide

const buf1 = Buffer.alloc(10).fill('abcd')//空間足夠,循環填充
console.log(buf1.toString())//abcdabcdab  循環填充,知道空間滿
const buf2 = Buffer.alloc(3).fill('abcdef');//空間不夠,截斷
console.log(buf2.toString());//abc
const buf3 = Buffer.alloc(10).fill('abc', 3);//從索引 3 開始填充
console.log(buf3);//<Buffer 00 00 00 61 62 63 61 62 63 61>
console.log(buf3.toString());//abcabcabca
const buf4 = Buffer.alloc(10).fill('abc', 3, 7);//從索引 3 開始填充,到索引 7 結束
console.log(buf4);//<Buffer 00 00 00 61 62 63 61 00 00 00>
console.log(buf4.toString());// abca
let buffer = Buffer.alloc(10).fill('abcd')
console.log(buffer.toString())
buffer=Buffer.alloc(10).fill(34) // 改變原來的 buffer
console.log(buffer.toString())

buffer 比較

buffer.equals(buffer)

比較兩個 buffer 的數據是否相同。函數

// 例子一:編碼同樣,內容相同
var buf1 = Buffer.from('A');
var buf2 = Buffer.from('A');

console.log( buf1.equals(buf2) );  // true

// 例子二:編碼同樣,內容不一樣
var buf3 = Buffer.from('A');
var buf4 = Buffer.from('B');

console.log( buf3.equals(buf4) );  // false

// 例子三:編碼不同,內容相同
var buf5 = Buffer.from('ABC');  // <Buffer 41 42 43>
var buf6 = Buffer.from('414243', 'hex');//<Buffer 41 42 43>
var buf7 = Buffer.from('414243', 'utf16le');//<Buffer 34 00 31 00 34 00 32 00 34 00 33 00>
console.log(buf5.equals(buf6));//true
console.log(buf7.equals(buf6));//false

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

compare 可規定比較的範圍,返回一個數字。

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

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

arr.sort(Buffer.compare) -- buffer 數組排序,按比較,第一位能比出結果的,就肯定了。

const buf1 = Buffer.from('81234');
const buf2 = Buffer.from('80234');
const arr = [buf1, buf2];
console.log(arr);//[ <Buffer 38 31 32 33 34>, <Buffer 38 30 32 33 34> ]
console.log(arr.sort(Buffer.compare));//[ <Buffer 38 30 32 33 34>, <Buffer 38 31 32 33 34> ] 第一位,38=38,不能得出順序,第二位,30 < 31,buf2 排在前面來。

檢查 buffer

Buffer.isBuffer(object)

計算須要分配的內存

Buffer.byteLength(string, encoding=‘utf8’)

buffer 大小 buffer.length

console.log(Buffer.byteLength('☃☃'))// 6 須要 6 個字節存儲 兩個☃☃ 
let buffer = Buffer.alloc(10).fill('☃',4)// 從索引 4 開始存,恰好能存 2 個 ☃
console.log(buffer.length)// 10 給 buffer 分配的內存空間
console.log('☃☃'.length)//2  字符串長度
console.log(buffer.toString())// '☃☃' 
console.log(buffer.toString().length)// 6 單位是字節
console.log(Buffer.byteLength('☃☃'))// 6 須要 6 個字節存儲兩個☃

let buffer2 = Buffer.alloc(10).fill('☃',5)// 從索引 5 開始存,恰好能存 2 個 ☃ 還差 1 字節空間
console.log(buffer2.toString())// '☃�' 有一個亂碼 
console.log(buffer2.toString().length)// 7

buffer 鏈接 Buffer.concat(bufferList[,totalLength])

totalLength 是全部bufferList 元素長度的累加。
totalLength > 實際累加長度,用 0 填充;
totalLength < 實際累計長度,後面的捨棄。

//接着上面的代碼
let buf3=Buffer.concat(arr,4);
console.log(buf3);//<Buffer 38 30 32 33 34 38> 捨棄四位
let buf4=Buffer.concat(arr,12);
console.log(buf4);//<Buffer 38 30 32 33 34 38 31 32 33 34 00 00> 0 填充兩位

複製 bufSource.copy(bufTarget[,targetStart[,ssourceStart[,sourceEnd]]])

複製 bufSource 的 sourceStart -- sourceEnd-1 的字節到 bufTarget 的 target 位置開始存放。
返回值int:實際存入的字節數。目標 buffer 空間不夠,複製源會被階段。

const buf1 = Buffer.alloc(10);//分配 10 個字節的空間
const buf2 = Buffer.from('copyFunction');
console.log('複製前 buf1', buf1);//複製前 buf1 <Buffer 00 00 00 00 00 00 00 00 00 00>

console.log('複製前 buf2', buf2);//複製前 buf2 <Buffer 63 6f 70 79 46 75 6e 63 74 69 6f 6e>

let result = buf2.copy(buf1, 4, 1, 5);//複製 buf1 1--5 字節到 buf2 的  第 4 個索引位置開始存放,用 6 個字節來存放4個字節的數據,空間足夠。
console.log('複製後 buf1', buf1);//複製後 buf1 <Buffer 00 00 00 00 6f 70 79 46 00 00>
console.log(buf1.toString());//opyF
console.log('複製後 buf2', buf2);//複製後 buf2 <Buffer 63 6f 70 79 46 75 6e 63 74 69 6f 6e>
console.log(buf2.toString());//copyFunction
console.log('複製後 result', result);// 4

截取 buf.slice([start=0[, end=buf.length]])

從 buf 中截取一部分,組成新的 buffer , 二者內存是共享的,因此修改時,會相互影響。

let buf1 = Buffer.alloc(5).fill('abcd')
let buf2 = buf1.slice()
console.log(buf2.toString())//abcda
let buf3 = buf1.slice(2,4)
console.log(buf3)//cd
console.log(buf3.toString())//cd
// 測試共享內存
console.log(buf3[0]='100')// 100 修改 buf3 的第一個值 爲 d,返回修改後的值
console.log(buf3[0].toString())//100
console.log(buf3)//<Buffer 64 64>
console.log(buf3.toString())//dd  修改了
console.log(buf1)//<Buffer 61 62 64 64 61>
console.log(buf1.toString())//abdd buf1 也修改了

查找 buf.indexOf(value,byteOffset=0)

從 buf 的 byteOffset 位置開始查找 value,找到一個 value,返回其索引,不然返回 -1。value 能夠是 StringIntBuffer

const buf2 = Buffer.from('copyFunction');
let result = buf2.indexOf('c', 3, 'utf8');
let result2 = buf2.indexOf('c');
let result3 = buf2.indexOf('C');
console.log(result);// 7 索引 3 以後第一個 c 的索引
console.log(result2);// 0 第一個 c
console.log(result3);// -1
buf2.indexOf(Buffer.from('copy'),2,'utf8');//-1
buf2.indexOf(9,4);//-1
let buffer = Buffer.alloc(10).fill('abcd');
console.log(buffer.toString());// abcdabcdab

// 遞歸查找全部buffer
let indexs = [];//這裏很關鍵 存儲查找到的下標
function recursiveIndexOf(buffer, char, start) {
    if (start < 0) { start = 0; }
    if (start > buffer.length - 1) { return -1; }// 開始下標大於 buffer 最大下標,返回 -1,也是遞歸出口
    let index = buffer.indexOf(char, start);
    if (index !== -1) {
        indexs.push(index);
        recursiveIndexOf(buffer, char, index + 1);
    }
    return indexs;
}
let result = recuisiveIndexOf(buffer, 'a', 0);
console.log(result);//[0,4,8]

buffer 轉 String 和 Object

buf.toString([encoding=utf8[,start=0[,end=buf.length]]])buf.toJSON()
toJSON 返回一個對象。{type:'Buffer',data:[]} data 是 buffer 的值。

let buffer = Buffer.alloc(10).fill('abcd');
console.log(buffer.toString())//abcd
console.log(buffer.toJSON())//{ type: 'Buffer',data: [ 97, 98, 99, 100, 97, 98, 99, 100, 97, 98 ] }
console.log(Object.getPrototypeOf(buffer.toJSON()))// {}  可見 toJSON 返回的是對象
console.log(buffer[0])//97
console.log(buffer.toJSON().data[0])//97
console.log(buffer.toJSON().data)//[ 97, 98, 99, 100, 97, 98, 99, 100, 97, 98 ]
console.log(JSON.stringify(buffer.toJSON()))// 變成 json 字符串

buffer 遍歷

buffer.keys()buffer.values()buffer.entries()

let buffer = Buffer.alloc(10).fill('abcd');
for (let key of buffer.keys()) {
    process.stdout.write(`${key}`)//輸出不換行,write 只能接收 String 和 Buffer 做爲參數,可用模板字符串轉換
    // console.log(key) 這樣輸出會換行
}
//0123456789
console.log()
for (let value of buffer.values()) {
    process.stdout.write(`${value}`);//9798991009798991009798
}
console.log('')
for (let entriy of buffer.entries()) {
    console.log('buffer[%d]==%d', entriy[0], entriy[1])
}
/* 
buffer[0]==97
buffer[1]==98
buffer[2]==99
buffer[3]==100
buffer[4]==97
buffer[5]==98
buffer[6]==99
buffer[7]==100
buffer[8]==97
buffer[9]==98 
*/

TODO

  • TypeArray vs Buffer vs ArrayBuffer

最後

第一次在 SG 寫用 markdown 寫文章,體驗並不怎麼好,必須字體很小,看的眼疼,代碼顯示不太友好,好比會出現滾動條,不帶行號等。SG 大佬衆多,歡迎指教。

參考文章:

相關文章
相關標籤/搜索