何為 Buffer
在ES6引入 TypeArray 之前,JS沒有能讀取和操作二進(jìn)制數(shù)據(jù)流的機(jī)制,Buffer 作為 Node API 引入,以便能和 TCP 網(wǎng)絡(luò)流、文件流等進(jìn)行交互。 目前 ES6 中有 TypeArray 了,Buffer 類以更加優(yōu)化和適用的于 Node 操作的方式實(shí)現(xiàn)了 Unit8Array API。
總之,Buffer 就是用來操作二進(jìn)制數(shù)據(jù)的,位于全局變量中,無需引入即可使用。
Buffer 實(shí)例類似于 整型數(shù)組,緩沖區(qū)大小在創(chuàng)建是確定,不能調(diào)整,內(nèi)存有C++申請,JS 分配。
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。
Buffer 緩存區(qū),計(jì)算機(jī)讀取速度和處理不匹配,讀取速度高于處理速度時(shí),會開辟一段內(nèi)存區(qū)域存放待處理的數(shù)據(jù),這個(gè)段內(nèi)存就叫緩沖區(qū)。
實(shí)例化 Buffer
在v6.0之前創(chuàng)建Buffer對象直接使用new Buffer()構(gòu)造函數(shù)來創(chuàng)建對象實(shí)例,但是Buffer對內(nèi)存的權(quán)限操作相比很大,可以直接捕獲一些敏感信息,存在安全隱患,之后的版本,用下面幾個(gè)函數(shù)實(shí)例畫一個(gè)Buffer:
- Buffer.from()
- Buffer.alloc()
- Buffer.allocUnsafe()
| 函數(shù)|參數(shù)|返回值|
|:--:|:--:|:--:|:--:|:--:|
|from|arry|包含array的字節(jié)副本的Buffer,數(shù)組中的每一項(xiàng)表示一個(gè)8位字節(jié)的數(shù)字,故值在0--255以內(nèi),否則取余|
|from|buffer|從buffer復(fù)制一個(gè)新的buffer|
|from|arrayBuffer[,byteOffet,[,length]]|與arrayBuffer共享內(nèi)存的Buffer|
|from|string[,encoding]|string初始化的Buffer|
|alloc|size[,fill[encoding]]|指定大小的Buffer實(shí)例額,省略 fill,默認(rèn)用0填充|
|allocUnsafe|size|指定大小的buffer,不被初始化,可能包含敏感信息|
給allocUnsafe分配的內(nèi)存不被初始化,即歸零,內(nèi)存速度快,但是可能包含舊數(shù)據(jù),不覆蓋這些數(shù)據(jù),就可能造成內(nèi)存泄漏。
編碼
支持以下編碼:
- utf
- ascii
- base64
- binary
- utf16le
- hex
讀寫緩沖區(qū)
buffer.write(string[offset,[length]][,encoding])
- string - 寫入緩沖區(qū)的字符串;
- offset - 開始寫入的位置,默認(rèn) 0;
- length - 寫入字節(jié),默認(rèn) buf.length
- encoding - 編碼,默認(rèn) utf8。
返回值:int:寫入的實(shí)際大小,沒有足夠的空間保存,只會寫入一部分。
buffer.toString([endcoding[,start=0[,end=buffer.length]]]) 解碼指定緩沖區(qū)的數(shù)據(jù),并按 endcoding 編碼格式返回字符串。
let buf = Buffer.alloc(10);//分配 10 個(gè)字節(jié)的空間
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個(gè)字節(jié)
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 個(gè)字節(jié)的內(nèi)存,用 10 填充
console.log(buf2)//<Buffer 0a 0a 0a 0a 0a 0a 0a 0a>
let size = buf2.write('this a buffer', 2, 2);//從索引 2 開始寫,寫入2字節(jié)的數(shù)據(jù)
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 可以是 Buffer 、String 、Int。
const buf1 = Buffer.alloc(10).fill('abcd')//空間足夠,循環(huán)填充
console.log(buf1.toString())//abcdabcdab 循環(huán)填充,知道空間滿
const buf2 = Buffer.alloc(3).fill('abcdef');//空間不夠,截?cái)?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 結(jié)束
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)
比較兩個(gè) buffer 的數(shù)據(jù)是否相同。
// 例子一:編碼一樣,內(nèi)容相同
var buf1 = Buffer.from('A');
var buf2 = Buffer.from('A');
console.log( buf1.equals(buf2) ); // true
// 例子二:編碼一樣,內(nèi)容不同
var buf3 = Buffer.from('A');
var buf4 = Buffer.from('B');
console.log( buf3.equals(buf4) ); // false
// 例子三:編碼不一樣,內(nèi)容相同
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 可規(guī)定比較的范圍,返回一個(gè)數(shù)字。
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 數(shù)組排序,按位比較,第一位能比出結(jié)果的,就確定了。
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)
計(jì)算需要分配的內(nèi)存
Buffer.byteLength(string, encoding=‘utf8’)
buffer 大小 buffer.length
console.log(Buffer.byteLength('??'))// 6 需要 6 個(gè)字節(jié)存儲 兩個(gè)??
let buffer = Buffer.alloc(10).fill('?',4)// 從索引 4 開始存,剛好能存 2 個(gè) ?
console.log(buffer.length)// 10 給 buffer 分配的內(nèi)存空間
console.log('??'.length)//2 字符串長度
console.log(buffer.toString())// '??'
console.log(buffer.toString().length)// 6 單位是字節(jié)
console.log(Buffer.byteLength('??'))// 6 需要 6 個(gè)字節(jié)存儲兩個(gè)?
let buffer2 = Buffer.alloc(10).fill('?',5)// 從索引 5 開始存,剛好能存 2 個(gè) ? 還差 1 字節(jié)空間
console.log(buffer2.toString())// '??' 有一個(gè)亂碼
console.log(buffer2.toString().length)// 7
buffer 連接 Buffer.concat(bufferList[,totalLength])
totalLength 是所有bufferList 元素長度的累加。
totalLength > 實(shí)際累加長度,用 0 填充;
totalLength < 實(shí)際累計(jì)長度,后面的舍棄。
//接著上面的代碼
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 填充兩位
復(fù)制 bufSource.copy(bufTarget[,targetStart[,ssourceStart[,sourceEnd]]])
復(fù)制 bufSource 的 sourceStart -- sourceEnd-1 的字節(jié)到 bufTarget 的 target 位置開始存放。
返回值int:實(shí)際存入的字節(jié)數(shù)。目標(biāo) buffer 空間不夠,復(fù)制源會被階段。
const buf1 = Buffer.alloc(10);//分配 10 個(gè)字節(jié)的空間
const buf2 = Buffer.from('copyFunction');
console.log('復(fù)制前 buf1', buf1);//復(fù)制前 buf1 <Buffer 00 00 00 00 00 00 00 00 00 00>
console.log('復(fù)制前 buf2', buf2);//復(fù)制前 buf2 <Buffer 63 6f 70 79 46 75 6e 63 74 69 6f 6e>
let result = buf2.copy(buf1, 4, 1, 5);//復(fù)制 buf1 1--5 字節(jié)到 buf2 的 第 4 個(gè)索引位置開始存放,用 6 個(gè)字節(jié)來存放4個(gè)字節(jié)的數(shù)據(jù),空間足夠。
console.log('復(fù)制后 buf1', buf1);//復(fù)制后 buf1 <Buffer 00 00 00 00 6f 70 79 46 00 00>
console.log(buf1.toString());//opyF
console.log('復(fù)制后 buf2', buf2);//復(fù)制后 buf2 <Buffer 63 6f 70 79 46 75 6e 63 74 69 6f 6e>
console.log(buf2.toString());//copyFunction
console.log('復(fù)制后 result', result);// 4
截取 buf.slice([start=0[, end=buf.length]])
從 buf 中截取一部分,組成新的 buffer , 兩者內(nèi)存是共享的,所以修改時(shí),會相互影響。
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
// 測試共享內(nèi)存
console.log(buf3[0]='100')// 100 修改 buf3 的第一個(gè)值 為 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][,encoding=‘utf8’])
從 buf 的 byteOffset 位置開始查找 value,找到一個(gè) value,返回其索引,否則返回 -1。value 可以是 String 、Int 、Buffer。
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 之后第一個(gè) c 的索引
console.log(result2);// 0 第一個(gè) 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 = [];//這里很關(guān)鍵 存儲查找到的下標(biāo)
function recursiveIndexOf(buffer, char, start) {
if (start < 0) { start = 0; }
if (start > buffer.length - 1) { return -1; }// 開始下標(biāo)大于 buffer 最大下標(biāo),返回 -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 轉(zhuǎn) String 和 Object
buf.toString([encoding=utf8[,start=0[,end=buf.length]]])、buf.toJSON()
toJSON 返回一個(gè)對象。{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 作為參數(shù),可用模板字符串轉(zhuǎn)換
// 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
最后
第一次在簡書發(fā)文章,體驗(yàn)不怎么好。markdown 不支持表格嗎??代碼也不會顯示行號。