bufio包官方文档
Go源码学习-索引目录
bufio
包是 Go 语言标准库中提供的一个缓冲 I/O 功能的工具包。它主要用于提供对 I/O 操作的缓冲支持,可以帮助提升 I/O 操作的性能和效率。
bufio
包提供了一组功能强大的工具,使得对数据的读取和写入变得更加高效、灵活,并且可以减少对系统资源的频繁占用,特别适用于处理大量数据或需要高性能的 I/O 操作场景。
具体来说,bufio
包提供了如下功能和作用:
缓冲读取(Buffered Reading):
bufio
包通过 Reader
类型提供了缓冲读取的功能,可以减少对底层数据源(比如文件、网络连接等)的实际读取次数,从而提高读取性能。缓冲写入(Buffered Writing):
Writer
类型,bufio
包支持缓冲写入,将数据暂时存储在内存中,减少对底层数据目标的实际写入次数,提高写入性能。高效的字节处理(Efficient Byte Handling):
行扫描(Line Scanning):
bufio.Scanner
类型可以方便地对文本进行分割,支持按行扫描,用户可以定义自己的分割函数,方便地处理各种文本格式。灵活性和性能提升:
// Package bufio 实现了带缓冲的 I/O 操作。它封装了一个 io.Reader 或 io.Writer
// 对象,创建了另一个实现相同接口的对象(Reader 或 Writer),但提供了缓冲和
// 一些文本 I/O 的辅助功能。
package bufio
import (
"bytes"
"errors"
"io"
"strings"
"unicode/utf8"
)
// 默认缓冲区大小
const (
defaultBufSize = 4096
)
var (
// ErrInvalidUnreadByte 表示对 UnreadByte 的无效使用
ErrInvalidUnreadByte = errors.New("bufio: invalid use of UnreadByte")
// ErrInvalidUnreadRune 表示对 UnreadRune 的无效使用
ErrInvalidUnreadRune = errors.New("bufio: invalid use of UnreadRune")
// ErrBufferFull 表示缓冲区已满
ErrBufferFull = errors.New("bufio: buffer full")
// ErrNegativeCount 表示计数为负值
ErrNegativeCount = errors.New("bufio: negative count")
)
// 缓冲输入。
// Reader 实现了对 io.Reader 对象的缓冲。它包装了一个由客户端提供的 io.Reader,
// 创建了另一个对象(Reader),该对象同样实现了接口,但提供了缓冲和一些文本 I/O 的帮助。
type Reader struct {
buf []byte // 缓冲区
rd io.Reader // 客户端提供的 reader
r, w int // buf 的读写位置
err error // 错误信息
lastByte int // 上一次读取的字节,用于 UnreadByte;-1 表示无效
lastRuneSize int // 上一次读取的 rune 大小,用于 UnreadRune;-1 表示无效
}
// 最小读取缓冲区大小
const minReadBufferSize = 16
// 最大连续空读取次数
const maxConsecutiveEmptyReads = 100
// NewReaderSize 返回一个新的 [Reader],其缓冲区大小至少为指定大小。如果参数 io.Reader
// 已经是一个拥有足够大大小的 [Reader],则返回底层 [Reader]。
func NewReaderSize(rd io.Reader, size int) *Reader {
// 它已经是一个 Reader 吗?
b, ok := rd.(*Reader)
if ok && len(b.buf) >= size {
return b
}
r := new(Reader)
r.reset(make([]byte, max(size, minReadBufferSize)), rd)
return r
}
// NewReader 返回一个新的 [Reader],其缓冲区大小为默认大小。
func NewReader(rd io.Reader) *Reader {
return NewReaderSize(rd, defaultBufSize)
}
上述注释解释了 bufio
包的目的和基本结构。
更详细的解释:
bufio
包实现了带缓冲的 I/O 操作,用于提高 I/O 操作的性能和效率。Reader
结构体是对 io.Reader
对象的缓冲实现。它包含一个缓冲区 buf
、一个客户端提供的 io.Reader
对象 rd
、以及读写位置和错误信息等字段。const
声明了一些常量,如默认缓冲区大小、最小读取缓冲区大小和最大连续空读取次数。var
声明了一些错误变量,用于表示在使用 Reader
过程中可能发生的错误情况。NewReaderSize
函数返回一个新的 Reader
,其缓冲区大小至少为指定大小。如果参数 io.Reader
已经是一个拥有足够大大小的 Reader
,则返回底层的 Reader
。NewReader
函数返回一个新的 Reader
,其缓冲区大小为默认大小。这部分代码定义了 Size
方法,该方法用于返回 Reader
结构体中底层缓冲区的大小(以字节为单位)。
// Size 返回底层缓冲区的大小(以字节为单位)。
func (b *Reader) Size() int { return len(b.buf) }
解释:
Size
方法是 Reader
结构体的方法,其接收者是 b
,类型是 Reader
。return len(b.buf)
,它返回 b
结构体中 buf
字段的长度,即底层缓冲区的大小。作用:
Reader
结构体底层缓冲区大小的接口。这部分代码定义了 Reset
方法,用于重置 Reader
结构体的状态,并切换缓冲区以从新的读取源 r
进行读取。
// Reset 丢弃任何已缓存的数据,重置所有状态,并切换缓冲区以从读取源 r 进行读取。
// 在 [Reader] 的零值上调用 Reset 会将内部缓冲区初始化为默认大小。
// 调用 b.Reset(b)(即将 [Reader] 重置为自身)不会执行任何操作。
func (b *Reader) Reset(r io.Reader) {
// 如果将读取源 r 传递给 NewReader,NewReader 将返回 r。
// 代码的不同层可能会这样做,然后稍后将 r 传递给 Reset。在这种情况下避免无限递归。
if b == r {
return
}
// 如果缓冲区为空,则将其初始化为具有默认大小的新字节切片。
if b.buf == nil {
b.buf = make([]byte, defaultBufSize)
}
// 调用 reset 方法,将缓冲区、读取源以及其他状态重置。
b.reset(b.buf, r)
}
解释:
Reset
方法是 Reader
结构体的方法,其接收者是 b
,类型是 Reader
。b
和新的读取源 r
相同,直接返回,以避免无限递归。buf
字段为 nil
,则将其初始化为一个具有默认大小的新字节切片。reset
方法,将缓冲区、读取源、上一个字节和上一个符文大小等状态重置。// reset 方法用于具体执行 [Reader] 结构体的重置,将各个字段设置为新的状态。
func (b *Reader) reset(buf []byte, r io.Reader) {
*b = Reader{
buf: buf,
rd: r,
lastByte: -1,
lastRuneSize: -1,
}
}
这个内部的 reset
方法用于具体执行 Reader
结构体的重置,将各个字段设置为新的状态。
作用:
Reset
方法的作用是在切换读取源或者需要丢弃任何已缓存的数据时,重置 Reader
结构体的状态,以便重新开始读取新的数据。Reset
方法被调用时传入的 r
与当前的 b
相同,表示正在尝试将 Reader
重置为自身,此时直接返回,不进行任何操作。Reader
是零值时(未使用 NewReader
初始化),调用 Reset
会将内部缓冲区初始化为默认大小。这部分代码定义了 fill
方法,用于在缓冲区中读取新的数据块。
var errNegativeRead = errors.New("bufio: reader returned negative count from Read")
// fill 读取一个新的数据块填充到缓冲区中。
func (b *Reader) fill() {
// 将现有数据滑动到开头。
if b.r > 0 {
copy(b.buf, b.buf[b.r:b.w])
b.w -= b.r
b.r = 0
}
// 如果缓冲区已满,则抛出 panic。
if b.w >= len(b.buf) {
panic("bufio: tried to fill full buffer")
}
// 读取新数据:尝试有限次数。
for i := maxConsecutiveEmptyReads; i > 0; i-- {
// 从读取源 b.rd 中读取新的数据块到缓冲区中。
n, err := b.rd.Read(b.buf[b.w:])
// 如果读取的字节数 n 小于零,抛出 panic,表示读取返回了负数。
if n < 0 {
panic(errNegativeRead)
}
// 将写指针 b.w 向前移动,表示成功读取了 n 个字节。
b.w += n
// 如果出现错误,将错误存储在 b.err 中,并结束方法。
if err != nil {
b.err = err
return
}
// 如果成功读取了至少一个字节,退出循环。
if n > 0 {
return
}
}
// 如果整个循环完成后仍未读取任何字节,将 b.err 设置为 io.ErrNoProgress。
b.err = io.ErrNoProgress
}
解释:
fill
方法是 Reader
结构体的方法,没有显式的接收者,但它使用了 b
结构体的字段。b.r
大于零,表示有未读的数据在缓冲区中,将这些数据移动到缓冲区的开头。maxConsecutiveEmptyReads
),尝试从读取源 b.rd
中读取新的数据块到缓冲区中。
n
小于零,抛出 panic,表示读取返回了负数。b.w
向前移动,表示成功读取了 n
个字节。b.err
中,并结束方法。b.err
设置为 io.ErrNoProgress
。作用:
fill
方法的主要作用是确保缓冲区中始终存在可读取的数据,以提高读取效率。// readErr 返回当前的错误并清空错误状态。
func (b *Reader) readErr() error {
err := b.err
b.err = nil
return err
}
解释:
readErr
方法是 Reader
结构体的方法,没有显式的接收者,但它使用了 b
结构体的字段。b.err
)并将错误状态清空。err
中,然后将 b.err
设置为 nil
。作用:
// Peek 返回下一个 n 个字节而不移动读取器的位置。
// 这些字节在下一次读取调用时不再有效。
// 如果 Peek 返回少于 n 个字节,它还会返回一个解释为为什么读取不完整的错误。
// 如果 n 大于 b 的缓冲区大小,则错误是 [ErrBufferFull]。
//
// 调用 Peek 会阻止 [Reader.UnreadByte] 或 [Reader.UnreadRune] 的调用成功,直到下一次读取操作。
func (b *Reader) Peek(n int) ([]byte, error) {
if n < 0 {
return nil, ErrNegativeCount
}
b.lastByte = -1
b.lastRuneSize = -1
// 当缓冲区中剩余数据不足 n 时,且缓冲区未满且没有错误时,调用 fill 方法填充缓冲区。
for b.w-b.r < n && b.w-b.r < len(b.buf) && b.err == nil {
b.fill() // b.w-b.r < len(b.buf) => buffer is not full
}
if n > len(b.buf) {
return b.buf[b.r:b.w], ErrBufferFull
}
// 0 <= n <= len(b.buf)
var err error
if avail := b.w - b.r; avail < n {
// 缓冲区中的数据不足
n = avail
err = b.readErr()
if err == nil {
err = ErrBufferFull
}
}
return b.buf[b.r : b.r+n], err
}
解释:
Peek
方法是 Reader
结构体的方法,没有显式的接收者,但它使用了 b
结构体的字段。n
个字节而不移动读取器的位置。Peek
返回的字节数少于 n
个,它还会返回一个解释为什么读取不完整的错误。n
大于 b
的缓冲区大小,则错误是 ErrBufferFull
。Peek
会阻止 Reader.UnreadByte
或 Reader.UnreadRune
的调用成功,直到下一次读取操作。作用:
Peek
方法用于预读取字节,允许查看但不消耗缓冲区中的数据。Peek
方法。// Discard 跳过接下来的 n 个字节,并返回丢弃的字节数。
// 如果 Discard 跳过的字节数少于 n 个,它也会返回一个错误。
// 如果 0 <= n <= b.Buffered(),则 Discard 保证在不从底层 io.Reader 读取的情况下成功执行。
func (b *Reader) Discard(n int) (discarded int, err error) {
if n < 0 {
return 0, ErrNegativeCount
}
if n == 0 {
return
}
b.lastByte = -1
b.lastRuneSize = -1
remain := n
for {
skip := b.Buffered()
if skip == 0 {
b.fill()
skip = b.Buffered()
}
if skip > remain {
skip = remain
}
b.r += skip
remain -= skip
if remain == 0 {
return n, nil
}
if b.err != nil {
return n - remain, b.readErr()
}
}
}
解释:
Discard
方法是 Reader
结构体的方法,没有显式的接收者,但它使用了 b
结构体的字段。n
个字节,并返回丢弃的字节数。Discard
跳过的字节数少于 n
个,它也会返回一个错误。0 <= n <= b.Buffered()
的情况下,Discard
保证在不从底层 io.Reader
读取的情况下成功执行。作用:
Discard
方法允许跳过指定数量的字节而不读取或返回它们,对于消费数据前不需要的部分是很有用的。io.Reader
中实际读取出来。