大家好,我是 frank。
欢迎大家点击标题下方蓝色文字「Golang 语言开发栈」关注公众号。
设为星标,第一时间接收推送文章。
文末扫码,加群一起学 Golang 语言。
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]。
标准库 bufio
的文档: https://pkg.go.dev/bufio@go1.20.2
标准库 bufio
的源码: https://cs.opensource.google/go/go/+/refs/tags/go1.20.2:src/bufio/
扫描二维码或回复「微信群」,加入微信群
点「赞」和「在看」是最大的支持👇
👇更多精彩内容,请点击「阅读原文」