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/