NodeJS中Buffer模塊詳解
一,開篇分析
所謂緩沖區(qū)Buffer,就是 "臨時(shí)存貯區(qū)" 的意思,是暫時(shí)存放輸入輸出數(shù)據(jù)的一段內(nèi)存。
JS語(yǔ)言自身只有字符串?dāng)?shù)據(jù)類型,沒有二進(jìn)制數(shù)據(jù)類型,因此NodeJS提供了一個(gè)與String對(duì)等的全局構(gòu)造函數(shù)Buffer來(lái)提供對(duì)二進(jìn)制數(shù)據(jù)的操作。除了可以讀取文件得到Buffer的實(shí)例外,還能夠直接構(gòu)造,例如:
var buffer = new Buffer([ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]) ;
Buffer與字符串類似,除了可以用.length屬性得到字節(jié)長(zhǎng)度外,還可以用[index]方式讀取指定位置的字節(jié),例如:
buffer[0] ; // 0x68;
Buffer與字符串能夠互相轉(zhuǎn)化,例如可以使用指定編碼將二進(jìn)制數(shù)據(jù)轉(zhuǎn)化為字符串:
var str = buffer.toString("utf-8"); // hello
將字符串轉(zhuǎn)換為指定編碼下的二進(jìn)制數(shù)據(jù):
var buffer= new Buffer("hello", "utf-8") ; // <Buffer 68 65 6c 6c 6f>
一點(diǎn)兒區(qū)別:
Buffer與字符串有一個(gè)重要區(qū)別。字符串是只讀的,并且對(duì)字符串的任何修改得到的都是一個(gè)新字符串,原字符串保持不變。
至于Buffer,更像是可以做指針操作的C語(yǔ)言數(shù)組。例如,可以用[index]方式直接修改某個(gè)位置的字節(jié)。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
slice方法也不是返回一個(gè)新的Buffer,而更像是返回了指向原Buffer中間的某個(gè)位置的指針,如下所示。
[ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]
^ ^
| |
bin bin.slice(2)
因此對(duì)slice方法返回的Buffer的修改會(huì)作用于原Buffer,例如:
var buffer= new Buffer([ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]) ;
var sub = bin.slice(2) ;
sub[0] = 0x65 ;
console.log(buffer) ; // <Buffer 68 65 65 6c 6f>
如果想要拷貝一份Buffer,得首先創(chuàng)建一個(gè)新的Buffer,并通過(guò).copy方法把原Buffer中的數(shù)據(jù)復(fù)制過(guò)去。
這個(gè)類似于申請(qǐng)一塊新的內(nèi)存,并把已有內(nèi)存中的數(shù)據(jù)復(fù)制過(guò)去。以下是一個(gè)例子。
var buffer= new Buffer([ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]) ;
var dup = new Buffer(bin.length) ;
buffer.copy(dup) ;
dup[0] = 0x48 ;
console.log(buffer) ; // <Buffer 68 65 6c 6c 6f>
console.log(dup) ; // <Buffer 48 65 65 6c 6f>
總之,Buffer將JS的數(shù)據(jù)處理能力從字符串?dāng)U展到了任意二進(jìn)制數(shù)據(jù)。
以上簡(jiǎn)單讓大家了解一下什么是Buffer,下面具體說(shuō)說(shuō)如何使用和具體使用場(chǎng)景。
二,聊聊Buffer
JavaScript對(duì)字符串處理十分友好,無(wú)論是寬字節(jié)還是單字節(jié)字符串,都被認(rèn)為是一個(gè)字符串。Node中需要處理網(wǎng)絡(luò)協(xié)議、操作數(shù)據(jù)庫(kù)、處理圖片、文件上傳等,還需要處理大量二進(jìn)制數(shù)據(jù),自帶的字符串遠(yuǎn)不能滿足這些要求,因此Buffer應(yīng)運(yùn)而生。
Buffer結(jié)構(gòu)
Buffer是一個(gè)典型的Javascript和C++結(jié)合的模塊,性能相關(guān)部分用C++實(shí)現(xiàn),非性能相關(guān)部分用javascript實(shí)現(xiàn)。
Node在進(jìn)程啟動(dòng)時(shí)Buffer就已經(jīng)加裝進(jìn)入內(nèi)存,并將其放入全局對(duì)象,因此無(wú)需require
Buffer對(duì)象:類似于數(shù)組,其元素是16進(jìn)制的兩位數(shù)。
Buffer內(nèi)存分配
Buffer對(duì)象的內(nèi)存分配不是在V8的堆內(nèi)存中,在Node的C++層面實(shí)現(xiàn)內(nèi)存的申請(qǐng)。
為了高效的使用申請(qǐng)來(lái)得內(nèi)存,Node中采用slab分配機(jī)制,slab是一種動(dòng)態(tài)內(nèi)存管理機(jī)制,應(yīng)用各種*nix操作系統(tǒng)。slab有三種狀態(tài):
(1) full:完全分配狀態(tài)
(2) partial:部分分配狀態(tài)
(3) empty:沒有被分配狀態(tài)
Buffer的轉(zhuǎn)換
Buffer對(duì)象可以和字符串相互轉(zhuǎn)換,支持的編碼類型如下:
ASCII、UTF-8、UTF-16LE/UCS-2、Base64、Binary、Hex
字符串轉(zhuǎn)Buffer
new Buffer(str, [encoding]),默認(rèn)UTF-8
buf.write(string, [offset], [length], [encoding])
Buffer轉(zhuǎn)字符串
buf.toString([encoding], [start], [end])
Buffer不支持的編碼類型
通過(guò)Buffer.isEncoding(encoding)判斷是否支持
iconv-lite:純JavaScript實(shí)現(xiàn),更輕量,性能更好無(wú)需C++到j(luò)avascript的轉(zhuǎn)換
iconv:調(diào)用C++的libiconv庫(kù)完成
Buffer的拼接
注意 "res.on('data', function(chunk) {})",其中的參數(shù)chunk是Buffer對(duì)象,直接用+拼接會(huì)自動(dòng)轉(zhuǎn)換為字符串,對(duì)于寬字節(jié)字符可能會(huì)導(dǎo)致亂碼產(chǎn)生,
解決方法:
(1) 通過(guò)可讀流中的setEncoding()方法,該方法可以讓data事件傳遞不再是Buffer對(duì)象,而是編碼后的字符串,其內(nèi)部使用了StringEncoder模塊。
(2) 將Buffer對(duì)象暫存到數(shù)組中,最后在組裝成一個(gè)大Buffer讓后編碼轉(zhuǎn)換為字符串輸出。
Buffer在文件I/O和網(wǎng)絡(luò)I/O中廣泛應(yīng)用,其性能舉足輕重,比普通字符串性能要高出很多。
Buffer的使用除了與字符串的轉(zhuǎn)換有性能損耗外,在文件讀取時(shí)候,有一個(gè)highWaterMark設(shè)置對(duì)性能影響至關(guān)重要。
a,highWaterMark設(shè)置對(duì)Buffer內(nèi)存的分配和使用有一定影響。
b, highWaterMark設(shè)置過(guò)小,可能導(dǎo)致系統(tǒng)調(diào)用次數(shù)過(guò)多。
什么時(shí)候該用buffer,什么時(shí)候不該用 ------ 純粹的javascript支持unicode碼而對(duì)二進(jìn)制不是很支持,當(dāng)解決TCP流或者文件流的時(shí)候,處理流是有必要的,我們保存非utf-8字符串,2進(jìn)制等等其他格式的時(shí)候,我們就必須得使用 ”Buffer“ 。
三,實(shí)例引入
var buf = new Buffer("this is text concat test !") ,str = "this is text concat test !" ;
console.time("buffer concat test !");
var list = [] ;
var len = 100000 * buf.length ;
for(var i=0;i<100000;i++){
list.push(buf) ;
len += buf.length ;
}
var s1 = Buffer.concat(list, len).toString() ;
console.timeEnd("buffer concat test !") ;
console.time("string concat test !") ;
var list = [] ;
for (var i = 100000; i >= 0; i--) {
list.push(str) ;
}
var s2 = list.join("") ;
console.timeEnd("string concat test !") ;
以下是運(yùn)行結(jié)果:
讀取速度肯定string更快,buffer還需要toString()的操作。 所以我們?cè)诒4孀址臅r(shí)候,該用string還是要用string,就算大字符串拼接string的速度也不會(huì)比buffer慢。
那什么時(shí)候我們又需要用buffer呢?沒辦法的時(shí)候,當(dāng)我們保存非utf-8字符串,2進(jìn)制等等其他格式的時(shí)候,我們就必須得使用了。
四,總結(jié)一下
(1),JavaScript適合處理Unicode編碼數(shù)據(jù),但對(duì)二進(jìn)制數(shù)據(jù)的處理并不友好。
(2),所以處理TCP流或文件系統(tǒng)時(shí),對(duì)八位字節(jié)流的處理很有必要。
(3),Node有幾個(gè)用于處理,創(chuàng)建和消耗八位字節(jié)流的方法。
(4),原始數(shù)據(jù)存放在一個(gè)Buffer實(shí)例中,一個(gè)Buffer類似一個(gè)整數(shù)數(shù)組,但是它的內(nèi)存,分配在V8堆棧外。一個(gè)Buffer的大小是不能更改的。
(5),處理的編碼類型有:ascii,utf8,utf16le,ucs2(utf16le的別名),base64,binary,hex。
(6),Buffer為全局元素,直接new Buffer()就得到一個(gè)Buffer實(shí)例。
相關(guān)文章
用npm-run實(shí)現(xiàn)自動(dòng)化任務(wù)的方法示例
這篇文章主要介紹了用npm-run實(shí)現(xiàn)自動(dòng)化任務(wù)的方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-01-01Node.js事件循環(huán)(Event Loop)和線程池詳解
這篇文章主要介紹了Node.js事件循環(huán)(Event Loop)和線程池詳解,這篇文章比較淺顯地探討了有關(guān)事件循環(huán)的內(nèi)部運(yùn)作機(jī)制和技術(shù)細(xì)節(jié),都是經(jīng)過(guò)深思熟慮的,需要的朋友可以參考下2015-01-01nodejs+koa2 實(shí)現(xiàn)模仿springMVC框架
這篇文章主要介紹了nodejs+koa2 實(shí)現(xiàn)模仿springMVC框架,本文通過(guò)實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10Node.js?操作本地文件及深入了解fs內(nèi)置模塊
這篇文章主要介紹了Node.js?操作本地文件及深入了解fs內(nèi)置模塊,node.js作為服務(wù)端應(yīng)用,肯定少不了對(duì)本地文件的操作,像創(chuàng)建一個(gè)目錄、創(chuàng)建一個(gè)文件、讀取文件內(nèi)容等都是我們開發(fā)中經(jīng)常需要用到的功能2022-09-09利用yarn代替npm管理前端項(xiàng)目模塊依賴的方法詳解
這篇文章主要給大家介紹了關(guān)于利用yarn代替npm管理前端項(xiàng)目模塊依賴的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-09-09nodejs后臺(tái)集成ueditor富文本編輯器的實(shí)例
本篇文章主要介紹了nodejs后臺(tái)集成ueditor富文本編輯器的實(shí)例,具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07Nodejs探秘之深入理解單線程實(shí)現(xiàn)高并發(fā)原理
這篇文章主要介紹了Nodejs單線程實(shí)現(xiàn)高并發(fā)原理,對(duì)Node.js感興趣的同學(xué),可以參考下2021-04-04