GoLang bytes.Buffer基礎(chǔ)使用方法詳解
一、bytes.Buffer的基礎(chǔ)知識
與strings.Builder
一樣,bytes.Buffer
也是開箱即用的。
bytes.Buffer
類型的用途主要是作為字節(jié)序列的緩沖區(qū)。
在內(nèi)部,bytes.Buffer
類型使用字節(jié)切片作為內(nèi)容容器,并有一個int類型的字段作為已讀計數(shù),這個已讀計數(shù)無法通過bytes.Buffer
提供的方法計算出來。
var buffer1 bytes.Buffer contents := "Simple byte buffer for marshaling data" // Write contents "Simple byte buffer for marshaling data" fmt.Printf("Write contents %q\n", contents) buffer1.WriteString(contents) // The length of buffer: 38 fmt.Printf("The length of buffer: %d\n", buffer1.Len()) // The capacity of buffer: 64 fmt.Printf("The capacity of buffer: %d\n", buffer1.Cap())
與strings.Reader
類型的Len方法一樣,buffer1的Len方法返回的也是內(nèi)容容器中未被讀取部分的長度,而不是其中已存內(nèi)容的總長度。
Buffer 值的長度是未讀內(nèi)容的長度,而不是已讀內(nèi)容的長度。
p1 := make([]byte, 7) n, _ := buffer1.Read(p1) // 7 bytes were read. (call Read) fmt.Printf("%d bytes were read. (call Read)\n", n) // The length of buffer: 31 fmt.Printf("The length of buffer: %d\n", buffer1.Len()) // The capacity of buffer: 64 fmt.Printf("The capacity of buffer: %d\n", buffer1.Cap())
Buffer值的容量是它的內(nèi)容容器(也就是那個字節(jié)切片)的容量,它只與當(dāng)前值之上的寫操作有關(guān),并隨著內(nèi)容的寫入而不斷增長。
二、bytes.Buffer類型的值已讀計數(shù)的作用
讀取內(nèi)容時,相應(yīng)方法會依據(jù)已讀計數(shù)找到未讀內(nèi)容,并在讀取之后更新計數(shù);
相應(yīng)方法包括所有名稱以Read開頭的方法,以及Next方法和WriteTo方法。
寫入內(nèi)容時,如需擴(kuò)容,相應(yīng)方法會根據(jù)已讀計數(shù)實現(xiàn)擴(kuò)容策略;
寫入時,如果沒有足夠的容量,就會對容器進(jìn)行擴(kuò)容。
擴(kuò)容時,方法會在必要時,依據(jù)已讀計數(shù)找到未讀部分,并把其中的內(nèi)容拷貝到擴(kuò)展容器的頭部位置。然后,方法會把已讀計數(shù)值置為0。
相應(yīng)方法包括所有名稱以Write開頭的方法,以及ReadFrom方法。
截斷內(nèi)容時,相應(yīng)方法截斷的時已讀計數(shù)代表索引之后的未讀部分;
截斷方法Truncate,接受一個int類型的參數(shù),表示在截斷時需要保留頭部多少個字節(jié)。
這里的頭部是未讀部分的頭部,而不是內(nèi)容容器的頭部。
這種情況下,已讀計數(shù)的值再加上參數(shù)值后得到的和,就是內(nèi)容容器新的總長度。
讀回退時,相應(yīng)方法會使用已讀計數(shù)記錄回退點;
用于讀回退的方法有UnreadByte和UnreadRune。這兩個方法分別用于回退一個字節(jié)和回退一個Unicode字符。
回退的前提是,在調(diào)用它們之前的那一個操作必須是“讀取”,并且是成功的讀取,否則這些方法就只會忽略后續(xù)操作并返回一個非nil的錯誤值。
只有緊挨在調(diào)用ReadRune方法之后,對UnreadRune方法對調(diào)用才能夠成功完成。
重置內(nèi)容時,相應(yīng)方法會把已讀計數(shù)置為0;
導(dǎo)出內(nèi)容時,相應(yīng)方法只會導(dǎo)出已讀計數(shù)代表的索引之后的未讀部分;
Buffer值的Bytes和String方法,只會訪問未讀部分的內(nèi)容,并返回相應(yīng)的結(jié)果值。
獲取長度時,相應(yīng)方法會依據(jù)已讀計數(shù)和內(nèi)容容器的長度,計算未讀部分的長度并返回;
Buffer值的Len方法返回的是內(nèi)容容器未讀部分的長度。
三、bytes.Buffer的擴(kuò)容策略
bytes.Buffer
既可以手動擴(kuò)容,也可以自動擴(kuò)容。除非完全確定后續(xù)內(nèi)容所需的字節(jié)數(shù),否則讓Buffer自動擴(kuò)容就好了。這兩種方式的擴(kuò)容策略一樣。
擴(kuò)容策略:
判斷內(nèi)容容器的剩余容量,是否滿足調(diào)用方的要求,是否足夠容納新的內(nèi)容;
如果剩余容量滿足容納新的內(nèi)容,就在當(dāng)前的內(nèi)容容器之上,進(jìn)行長度擴(kuò)容;
buf = buf[:length+need]
如果剩余容量不滿足容納新的內(nèi)容,就會用新的內(nèi)容容器去替代原有的內(nèi)容容器,從而實現(xiàn)擴(kuò)容;
這里有一個優(yōu)化,如果當(dāng)前內(nèi)容容器的容量的一半,仍然大于或等于現(xiàn)有長度(即未讀字節(jié)數(shù))再加上另需字節(jié)數(shù)的和,即:
cap(buf)/2 >= len(buf) + need
那么擴(kuò)容代碼就會復(fù)用現(xiàn)有的內(nèi)容容器,并把容器中的未讀內(nèi)容拷貝到它的頭部位置。
這意味著,其中的已讀內(nèi)容,將會全部被未讀內(nèi)容和之后的新內(nèi)容覆蓋掉。
如果當(dāng)前內(nèi)容容器的容量小于新長度的二倍。這時,就會把原有容器中的未讀內(nèi)容拷貝進(jìn)去,最后再用新的容器替換掉原有的容器。這個新容器將會等于原有容量的二倍,再加上另需字節(jié)數(shù)的和。
新容器的容量 = 原有容量 * 2 + 所需字節(jié)數(shù)
擴(kuò)容還會把已讀計數(shù)置為0。
對于處于零值狀態(tài)的Buffer值來說,如果第一次擴(kuò)容時另需的字節(jié)數(shù)小于等于64,那么該值就會基于一個預(yù)先定義好的、長度為64的字節(jié)數(shù)組來創(chuàng)建內(nèi)容容器。
這種情況下,容器的容量就是64。這樣做的目的是為了讓Buffer值在剛被真正使用的時候,可以快速的做好準(zhǔn)備。
四、bytes.Buffer的哪些方法會造成內(nèi)容的泄露
這里的內(nèi)容泄露是指,使用Buffer值的一方通過某種非標(biāo)準(zhǔn)的方式,得到本不該得到的內(nèi)容。
在bytes.Buffer
中,Bytes方法和Next方法都有可能會造成內(nèi)容的泄露。原因在于,它們都把基于內(nèi)容容器的切片直接返回給了方法的調(diào)用方。
通過切片,我們可以直接訪問和操縱它們的底層數(shù)組,不論這個切片是基于某個數(shù)組得來的,還是痛哦過對另一個切片做切片操作獲得的,都是如此。
bytes.Buffer
的Bytes方法和Next方法返回的字節(jié)切片,都是通過對內(nèi)容容器的切片做切片操作得到的。
contents := "ab" buffer1 := bytes.NewBufferString(contents) // The capacity of new buffer with contents "ab": 8 // 容量為何為8,看 runtime/string.go#stringtoslicebyte() fmt.Printf("The capacity of new buffer with contents %q: %d\n", contents, buffer1.Cap()) unreadBytes := buffer1.Bytes() // The unread bytes of the buffer: [97 98] fmt.Printf("The unread bytes of the buffer: %v\n", unreadBytes) buffer1.WriteString("cdefg") // The capacity of new buffer with contents "ab": 8 fmt.Printf("The capacity of new buffer with contents %q: %d\n", contents, buffer1.Cap()) unreadBytes = unreadBytes[:cap(unreadBytes)] // 基于前面的內(nèi)容獲取到結(jié)果值 // The unread bytes of the buffer: [97 98 99 100 101 102 103 0] fmt.Printf("The unread bytes of the buffer: %v\n", unreadBytes) // 操縱buffer unreadBytes[len(unreadBytes)-2] = byte('X') // The unread bytes of the buffer: [97 98 99 100 101 102 88 0] fmt.Printf("The unread bytes of the buffer: %v\n", unreadBytes)
到此這篇關(guān)于GoLang bytes.Buffer基礎(chǔ)使用方法詳解的文章就介紹到這了,更多相關(guān)Go bytes.Buffer內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang?struct?json?tag的使用以及深入講解
這篇文章主要給大家介紹了關(guān)于golang?struct?json?tag的使用以及深入講解,文中通過實例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2022-02-02Go語言從單體服務(wù)到微服務(wù)設(shè)計方案詳解
這篇文章主要為大家介紹了Go語言從單體服務(wù)到微服務(wù)設(shè)計方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03golang如何實現(xiàn)mapreduce單進(jìn)程版本詳解
這篇文章主要給大家介紹了關(guān)于golang如何實現(xiàn)mapreduce單進(jìn)程版本的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-01-01如何使用Golang創(chuàng)建與讀取Excel文件
我最近工作忙于作圖,圖表,需要自己準(zhǔn)備數(shù)據(jù)源,所以經(jīng)常和Excel打交道,下面這篇文章主要給大家介紹了關(guān)于如何使用Golang創(chuàng)建與讀取Excel文件的相關(guān)資料,需要的朋友可以參考下2022-07-07淺析Go語言如何避免數(shù)據(jù)競爭Data?Race和競態(tài)條件Race?Condition
在并發(fā)編程中,數(shù)據(jù)競爭?(Data?Race)?和?競態(tài)條件?(Race?Condition)?是兩個常見的問題,本文將簡單介紹一下二者如何避免,有需要的可以了解下2025-01-01