?01 介紹
Go 語言標準庫 bufio? 是基于 Go 語言標準庫 io? 實現的,查看源碼可以發現,實際上它是包裝了 io.Reader? 接口和 io.Writer 接口,并且實現它們。
bufio 顧名思義,就是在緩沖區讀寫數據,比直接讀寫文件或網絡中的數據,性能更好些。
本文我們介紹 bufio? 的相關內容,建議讀者朋友們最好是先了解一下 io 的相關內容。
02 標準庫 bufio 的數據類型
查看標準庫 `bufio` 的文檔[1],它的數據類型主要有 bufio.Reader、bufio.Writer、bufio.ReadWriter? 和 bufio.Scanner。
我們以 bufio.Reader 為例,介紹它的數據結構、初始化方式和提供的方法。
bufio.Reader 的數據結構:
type Reader struct {
buf []byte
rd io.Reader
r, w int
err error
lastByte int
lastRuneSize int
}
閱讀源碼,我們可以發現 bufio.Reader 中包含的字段:
- buf []byte 緩沖區。
- rd io.Reader 緩沖區的數據源。
- r,w int 緩沖區讀寫索引位置。
- err error 錯誤。
- lastByte int 未讀字節的上一個字節。
- lastRuneSize 未讀字符的上一個字符的大小。
bufio.Reader 的初始化方式:
使用 bufio.Reader? 時,需要先初始化,bufio? 包提供了兩個初始化的函數,分別是 NewReaderSize? 和 NewReader。
func NewReaderSize(rd io.Reader, size int) *Reader {
// Is it already a Reader?
b, ok := rd.(*Reader)
if ok && len(b.buf) >= size {
return b
}
if size < minReadBufferSize {
size = minReadBufferSize
}
r := new(Reader)
r.reset(make([]byte, size), rd)
return r
}
func NewReader(rd io.Reader) *Reader {
return NewReaderSize(rd, defaultBufSize)
}
閱讀源碼,我們可以發現這兩個函數的返回值都是 *bufio.Reader 類型。
其中 NewReader? 是包裝了 NewReaderSize 函數,給定了一個默認值 4096,設置讀緩沖區的大小。
如果我們使用默認值,一般選擇使用 NewReader 函數。
如果不想使用默認值,可以選擇使用 NewReaderSize 函數。
bufio.Reader 提供的方法:
bufio.Reader? 提供了 15 個方法,我們介紹兩個比較常用的方法,分別是 Read? 和 ReadBytes。
func (b *Reader) Read(p []byte) (n int, err error) {
// 省略代碼 ...
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.w += n
}
// copy as much as we can
// Note: if the slice panics here, it is probably because
// the underlying reader returned a bad count. See issue 49795.
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
}
閱讀源碼,我們可以發現 Read? 方法是將緩沖區中的數據,讀取到 p 中,并返回讀取的字節大小和錯誤。
func (b *Reader) ReadBytes(delim byte) ([]byte, error) {
full, frag, n, err := b.collectFragments(delim)
// Allocate new buffer to hold the full pieces and the fragment.
buf := make([]byte, n)
n = 0
// Copy full pieces and fragment in.
for i := range full {
n += copy(buf[n:], full[i])
}
copy(buf[n:], frag)
return buf, err
}
閱讀源碼,我們可以發現 ReadBytes? 方法是讀取緩沖區中的數據截止到分隔符 delim 的位置,并返回數據和錯誤。
使用示例:
Read 方法
func main() {
f, _ := os.Open("/Users/frank/GolandProjects/go-package/lesson14/file.txt")
defer f.Close()
r := bufio.NewReader(f)
p := make([]byte, 12)
index, _ := r.Read(p)
fmt.Println(index)
fmt.Println(string(p[:index]))
}
需要注意的是,p 字節切片的長度,一個中文字符是 3 個字節,一個英文字符是 1 個字節。
ReadBytes 方法
func main() {
f, _ := os.Open("/Users/frank/GolandProjects/go-package/lesson14/file.txt")
defer f.Close()
r := bufio.NewReader(f)
bs, _ := r.ReadBytes('\n')
fmt.Println(string(bs))
}
需要注意的是,分隔符參數是 byte 類型,使用單引號。
03 總結
本文我們以 bufio.Reader? 為例,介紹標準庫 bufio 的數據類型、初始化方式和提供的方法。
實際上標準庫 bufio 使用非常簡單,但是想要避免踩 “坑”,讀者朋友們最好是熟讀標準庫 `bufio` 的源碼[2]。
參考資料[1]標準庫 bufio? 的文檔: https://pkg.go.dev/bufio@go1.20.2
[2]標準庫 bufio? 的源碼: https://cs.opensource.google/go/go/+/refs/tags/go1.20.2:src/bufio/