Go 語言標準庫 bufio 詳解
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/
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/aP9bT4UL1MK5EDx18WfvmQ