介绍
Go 语言标准库 bufio
是基于 Go 语言标准库 io
实现的,查看源码可以发现,实际上它是包装了 io.Reader
接口和 io.Writer
接口,并且实现它们。
bufio
顾名思义,就是在缓冲区读写数据,比直接读写文件或网络中的数据,性能更好些。
本文我们介绍 bufio
的相关内容,建议读者朋友们最好是先了解一下 io
的相关内容。
查看标准库 `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/