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/