介紹
package bufio也是io的一部分,但在不同包中,因此獨立一節(jié)。
其中包含bufio.go,scan.go兩部分。
bufio
bufio的作用是為一個已有的Reader或者Writer提供緩沖,我們知道操作系統(tǒng)的io是資源瓶頸,應該盡可能少的調(diào)用io操作,所以把大批量的數(shù)據(jù)一起讀取或?qū)懭胧歉玫倪x擇。使用方法:
w := bufio.NewWriter(os.Stdout)
fmt.Fprint(w, "Hello, ")
fmt.Fprint(w, "world!")
w.Flush() // Don't forget to flush!
// Output: Hello, world!
源碼中對Reader和Writer做了一個簡單封裝,bufio.Reader為例:
// Reader implements buffering for an io.Reader object.
type Reader struct {
buf []byte
rd io.Reader // reader provided by the client
r, w int // buf read and write positions
err error
lastByte int
lastRuneSize int
}
除了包括原始的reader,還有一個[]byte結(jié)構(gòu),過程以Read方法為例:
// Read reads data into p.
// It returns the number of bytes read into p.
// The bytes are taken from at most one Read on the underlying Reader,
// hence n may be less than len(p).
// At EOF, the count will be zero and err will be io.EOF.
func (b *Reader) Read(p []byte) (n int, err error) {
n = len(p)
if n == 0 {
return 0, b.readErr()
}
if b.r == b.w {
if b.err != nil {
return 0, b.readErr()
}
if len(p) >= len(b.buf) {
// Large read, empty buffer.
// Read directly into p to avoid copy.
n, b.err = b.rd.Read(p)
if n < 0 {
panic(errNegativeRead)
}
if n > 0 {
b.lastByte = int(p[n-1])
b.lastRuneSize = -1
}
return n, b.readErr()
}
b.fill() // buffer is empty
if b.r == b.w {
return 0, b.readErr()
}
}
// copy as much as we can
n = copy(p, b.buf[b.r:b.w])
b.r += n
b.lastByte = int(b.buf[b.r-1])
b.lastRuneSize = -1
return n, nil
}
解釋:每次讀取只調(diào)用內(nèi)部reader的一次操作,
如果內(nèi)部的buf小于提供的p,那么直接讀取到p里,不經(jīng)過buf。
如果buf更大,做一次fill操作:1.清理buf中的遺留數(shù)據(jù)到buf頭部,2.讀取內(nèi)部reader到buf,并向后移動w,w+=n
最后做了一次copy操作,將buf的內(nèi)容copy到p中。
再以Writer.Write為例:
// Write writes the contents of p into the buffer.
// It returns the number of bytes written.
// If nn < len(p), it also returns an error explaining
// why the write is short.
func (b *Writer) Write(p []byte) (nn int, err error) {
for len(p) > b.Available() && b.err == nil {
var n int
if b.Buffered() == 0 {
// Large write, empty buffer.
// Write directly from p to avoid copy.
n, b.err = b.wr.Write(p)
} else {
n = copy(b.buf[b.n:], p)
b.n += n
b.flush()
}
nn += n
p = p[n:]
}
if b.err != nil {
return nn, b.err
}
n := copy(b.buf[b.n:], p)
b.n += n
nn += n
return nn, nil
}
解釋:
n指buf中已經(jīng)寫了多少字節(jié)
b.Available()指buf中還剩多少字節(jié)可寫,等于len(buf)-n
b.Buffered()就是n
來看過程:
首先如果n=0,那么直接把p寫入到內(nèi)部writer
如果buf中有東西,那么把p的內(nèi)容先copy到buf中,并做一次flush(即buf寫入writer)
只要buf中沒有足夠的空間(小于len(p)),都會持續(xù)的寫入writer。
最后一點點尾巴,只能暫時留在buf里,等待下一次flush操作了。
在使用的場景中來看bufio.Writer的用途:
小buf,大量的寫入數(shù)據(jù):這樣就類似于不加這個buf,只留一點點尾巴。
大buf,小數(shù)據(jù)寫入:這樣就有可能不寫入,只是把數(shù)據(jù)先放到buf里。
Scanner
Scanner的作用是對一個Reader進行迭代,使用方式如下:
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
fmt.Println(scanner.Text()) // Println will add back the final '\n'
}
默認按照一行一行進行讀取,每次scan,scanner.Text()都會返回下一行的數(shù)據(jù),直到EOF,Scan()返回false。
我們來看源碼,主要是Scanner結(jié)構(gòu):
type Scanner struct {
r io.Reader // The reader provided by the client.
split SplitFunc // The function to split the tokens.
maxTokenSize int // Maximum size of a token; modified by tests.
token []byte // Last token returned by split.
buf []byte // Buffer used as argument to split.
start int // First non-processed byte in buf.
end int // End of data in buf.
err error // Sticky error.
empties int // Count of successive empty tokens.
scanCalled bool // Scan has been called; buffer is in use.
done bool // Scan has finished.
}
每次返回的『一行』(其實未必是一行,暫且這么叫)稱為token,移動到下一個token稱為一次advance,通過split函數(shù)做tokenize。其他都是一些比較明顯的輔助字段。
這里主要是這個split函數(shù),默認的bufio.NewScanner()代碼如下:
func NewScanner(r io.Reader) *Scanner {
return &Scanner{
r: r,
split: ScanLines,
maxTokenSize: MaxScanTokenSize,
}
}
以分行函數(shù)作為split,同時看到MaxScanTokenSize = 64 * 1024,也就是說一行不能太長。否則會拋錯,除非使用scanner.Buffer()方法自己提供緩沖區(qū)和最大容量。
除了默認的ScanLines,系統(tǒng)還提供了ScanRunes,ScanWords,ScanBytes三個split函數(shù),用戶也可以自定義split函數(shù)。