go
在提供了io
包的同時也提供了bufio
包來實現有緩存的讀寫操做以提升讀寫性能。爲何bufio
性能比io
高呢?緩存
// 默認緩衝區大小 const ( defaultBufSize = 4096 ) // 最小緩衝區大小 自定義小於次閾值將會被覆蓋 const minReadBufferSize = 16 // 使用默認緩衝區大小 bufio.NewReader(rd io.Reader) // 使用自定義緩衝區大小 bufio.NewReaderSize(rd io.Reader, size int)
緩衝讀
的大體過程以下,設定好緩衝區大小buf_size
後,讀取的字節數爲rn
,緩衝的字節數爲bn
:性能
rn >= buf_size
,則直接從文件讀取,不啓用緩衝。rn < buf_size
,則從文件讀取buf_size
字節的內容到緩衝區,程序再從緩衝區中讀取rn
字節的內容,此時緩衝區剩餘bn = buf_size - rn
字節。rn < bn
,則從緩衝區讀取rn
字節的內容,不發生文件IO
。rn >= bn
,則從緩衝區讀取bn
字節的內容,不發生文件IO
,緩衝區置爲空,迴歸1/2
步驟。緩衝讀經過預讀,能夠在必定程度上減小文件IO
次數,故提升性能。code
代碼演示:對象
package main import ( "bufio" "fmt" "strings" ) func main() { // 用 strings.Reader 模擬一個文件IO對象 strReader := strings.NewReader("12345678901234567890123456789012345678901234567890") // go 的緩衝區最小爲 16 byte,咱們用最小值比較容易演示 bufReader := bufio.NewReaderSize(strReader, 16) // bn = 0 但 rn >= buf_size 緩衝區不啓用 發生文件IO tmpStr := make([]byte, 16) n, _ := bufReader.Read(tmpStr) // bufReader buffered: 0, content: 1234567890123456 fmt.Printf("bufReader buffered: %d, content: %s\n", bufReader.Buffered(), tmpStr[:n]) // bn = 0 rn < buf_size 緩衝區啓用 // 緩衝區從文件讀取 buf_size 字節 發生文件IO // 程序從緩衝區讀取 rn 字節 // 緩衝區剩餘 bn = buf_size - rn 字節 tmpStr = make([]byte, 15) n, _ = bufReader.Read(tmpStr) // bufReader buffered: 1, content: 789012345678901 fmt.Printf("bufReader buffered: %d, content: %s\n", bufReader.Buffered(), tmpStr[:n]) // bn = 1 rn > bn // 程序從緩衝區讀取 bn 字節 緩衝區置空 不發生文件IO // 注意這裏只能讀到一個字節 tmpStr = make([]byte, 10) n, _ = bufReader.Read(tmpStr) // bufReader buffered: 0, content: 2 fmt.Printf("bufReader buffered: %d, content: %s\n", bufReader.Buffered(), tmpStr[:n]) // bn = 0 rn < buf_size 啓用緩衝讀 發生文件IO // 緩衝區從文件讀取 buf_size 字節 // 程序從緩衝區讀取 rn 字節 // 緩衝區剩餘 bn = buf_size - rn 字節 tmpStr = make([]byte, 10) n, _ = bufReader.Read(tmpStr) // bufReader buffered: 6, content: 3456789012 fmt.Printf("bufReader buffered: %d, content: %s\n", bufReader.Buffered(), tmpStr[:n]) // bn = 6 rn <= bn // 則程序衝緩衝區讀取 rn 字節 不發生文件IO tmpStr = make([]byte, 3) n, _ = bufReader.Read(tmpStr) // bufReader buffered: 3, content: 345 fmt.Printf("bufReader buffered: %d, content: %s\n", bufReader.Buffered(), tmpStr[:n]) // bn = 3 rn <= bn // 則程序衝緩衝區讀取 rn 字節 不發生文件IO tmpStr = make([]byte, 3) n, _ = bufReader.Read(tmpStr) // bufReader buffered: 0, content: 678 fmt.Printf("bufReader buffered: %d, content: %s\n", bufReader.Buffered(), tmpStr[:n]) }
要注意的是當緩衝區中有內容時,程序的這次讀取都會從緩衝區讀,而不會發生文件IO。只有當緩衝區爲空時,纔會發生文件IO。若是緩衝區的大小足夠,則啓用緩衝讀,先將內容載入填滿緩衝區,程序再從緩衝區中讀取。若是緩衝區太小,則會直接從文件讀取,而不使用緩衝讀。string
// 使用 defaultBufSize 大小 func NewWriter(w io.Writer) // 若是 size <= 0 則使用 defaultBufSize func NewWriterSize(w io.Writer, size int)
緩衝寫
的大體過程以下,設定好緩衝區大小buf_size
後,寫入的字節數爲wn
,緩衝的字節數爲bn
:it
wn >= buf_size
,則直接寫入文件,不啓用緩衝,發生文件IO。wn < buf_size
,則程序將內容寫入緩衝區,不發生文件IO。wn + bn < buf_size
,則程序將內容寫入緩衝區,不發生文件IO。wn + bn >= buf_size
,則緩衝區將buf_size
字節內容寫入文件,緩衝區wn + bn - buf_size
的剩餘內容。簡單說就是要寫入的內容先緩衝着,緩衝不下了則將緩衝區內容寫入文件。io
代碼演示:class
package main import ( "bufio" "fmt" "io" "strings" ) // 自定義一個 io.Writer 對象 type StringWriter struct { } func (s StringWriter) Write(p []byte) (n int, err error) { fmt.Printf("io write: %s\n", p) return len(p), nil } func main() { strReader := strings.NewReader("12345678901234567890") bufReader := bufio.NewReader(strReader) // 自定義的 io.Writer 對象 var stringWriter io.Writer stringWriter = StringWriter{} // 寫緩衝大小爲 6 爲了更好的演示咱們自定義了一個 io.Writer bufWriter := bufio.NewWriterSize(stringWriter, 6) tmpStr := make([]byte, 8) for true { rn, err := bufReader.Read(tmpStr) if nil != err && io.EOF == err { break } _, err = bufWriter.Write(tmpStr[:rn]) fmt.Printf("\nread and write: %s\n", tmpStr[:rn]) fmt.Printf("bufWriter buffered: %d, available: %d, size: %d\n", bufWriter.Buffered(), bufWriter.Available(), bufWriter.Size()) fmt.Printf("----------------------\n") } // 緩衝區最後剩餘的一些內容 _ = bufWriter.Flush() }
go run main.go // 沒有發生寫動做 '1234' 被緩衝 read and write: 1234 bufWriter buffered: 4, available: 2, size: 6 ---------------------- io write: 123456 // '12345678' 緩衝區滿 則會寫 '123456',繼續緩衝 '78' read and write: 5678 bufWriter buffered: 2, available: 4, size: 6 ---------------------- // 沒有發生寫動做 '789012' 被緩衝 read and write: 9012 bufWriter buffered: 6, available: 0, size: 6 ---------------------- io write: 789012 // '7890123456' 緩衝區滿 則會寫 '789012',繼續緩衝 '3456' read and write: 3456 bufWriter buffered: 4, available: 2, size: 6 ---------------------- io write: 345678 // '34567890' 緩衝區滿 則會寫 '345678',繼續緩衝 '90' read and write: 7890 bufWriter buffered: 2, available: 4, size: 6 ---------------------- // 文件讀取完畢,輸出剩餘的 '90' io write: 90