bufio包学习的上一篇文章:Go源码学习:bufio包 - 1.1 - bufio.go -(1)
// Read 读取数据到 p 中。
// 它返回读取到 p 中的字节数。
// 这些字节最多来自底层 [Reader] 的一次读取,
// 因此 n 可能小于 len(p)。
// 要精确读取 len(p) 字节,请使用 io.ReadFull(b, p)。
// 如果底层 [Reader] 可以在 io.EOF 时返回非零计数,
// 则此 Read 方法也可以这样做;参见 [io.Reader] 文档。
func (b *Reader) Read(p []byte) (n int, err error) {
n = len(p)
if n == 0 {
if b.Buffered() > 0 {
return 0, nil
}
return 0, b.readErr()
}
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()
}
// One read.
// Do not use b.fill, which will loop.
b.r = 0
b.w = 0
n, b.err = b.rd.Read(b.buf)
if n < 0 {
panic(errNegativeRead)
}
if n == 0 {
return 0, 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
方法是 Reader
结构体的方法,没有显式的接收者,但它使用了 b
结构体的字段。p
中。n
表示读取到 p
中的字节数。n
为零,且缓冲区中有数据,则直接返回零,表示成功读取了零字节。Reader
中读取新数据。b.r
等于写指针 b.w
,表示缓冲区为空,需要从底层 Reader
中读取数据。
b.err
,返回读取错误。p
的长度大于等于缓冲区的长度 len(b.buf)
,采用直接读取到 p
的方式,避免中间复制。b.fill
避免循环调用。copy
函数将尽可能多的数据从缓冲区复制到切片 p
中。b.r
,记录最后一个字节和最后一个符文的大小。n
。作用:
Read
方法用于从 Reader
中读取数据到指定的切片中。Reader
中读取数据。// ReadByte 读取并返回一个字节。
// 如果没有可用的字节,则返回错误。
func (b *Reader) ReadByte() (byte, error) {
b.lastRuneSize = -1
for b.r == b.w {
if b.err != nil {
return 0, b.readErr()
}
b.fill() // 缓冲区为空时填充
}
c := b.buf[b.r]
b.r++
b.lastByte = int(c)
return c, nil
}
解释:
ReadByte
是 Reader
结构体的方法,用于读取并返回一个字节。fill
方法进行填充。b.r
是否等于写指针 b.w
,如果相等表示缓冲区为空,需要填充。b.err
,则返回读取错误。c
,并将读指针 b.r
向前移动。nil
错误。作用:
ReadByte
方法用于从缓冲区中读取一个字节。// UnreadByte 撤销最近读取的字节。只有最近读取的字节可以被撤销。
//
// 如果最近调用 [Reader] 上的方法不是读取操作,则 UnreadByte 会返回错误。
// 特别地,[Reader.Peek]、[Reader.Discard] 和 [Reader.WriteTo] 不被认为是读取操作。
func (b *Reader) UnreadByte() error {
// 如果最近的字节值为负值(表示未读取),或者读指针为0但写指针大于0(表示有未读取的数据),返回无效的撤销字节错误。
if b.lastByte < 0 || b.r == 0 && b.w > 0 {
return ErrInvalidUnreadByte
}
// 如果读指针大于0,表示有已读取的数据,将读指针前移一位。
if b.r > 0 {
b.r--
} else {
// 如果读指针为0且写指针为0,表示缓冲区为空,将写指针设置为1。
b.w = 1
}
// 将最近读取的字节值写回缓冲区的读指针位置。
b.buf[b.r] = byte(b.lastByte)
// 重置最近读取的字节和最近 rune 大小的状态。
b.lastByte = -1
b.lastRuneSize = -1
return nil
}
解释:
UnreadByte
方法是 Reader
结构体的方法,用于撤销最近读取的字节。作用:
UnreadByte
方法允许撤销最近成功读取的字节,将读指针前移,使得该字节重新可用。// ReadRune 读取单个UTF-8编码的Unicode字符,并返回该字符及其字节大小。
// 如果编码的字符无效,它会消耗一个字节并返回unicode.ReplacementChar(U+FFFD)和大小为1。
func (b *Reader) ReadRune() (r rune, size int, err error) {
// 循环直到缓冲区中有足够的数据以确保完整的rune被读取,
// 或者达到缓冲区末尾,或者遇到错误。
for b.r+utf8.UTFMax > b.w && !utf8.FullRune(b.buf[b.r:b.w]) && b.err == nil && b.w-b.r < len(b.buf) {
b.fill() // b.w-b.r < len(buf) => buffer is not full
}
// 重置最近rune的大小。
b.lastRuneSize = -1
// 如果读取指针达到写指针,表示缓冲区为空,返回读取错误。
if b.r == b.w {
return 0, 0, b.readErr()
}
// 读取一个rune,并设置默认大小为1。
r, size = rune(b.buf[b.r]), 1
// 如果rune的值大于或等于utf8.RuneSelf,则表示可能占用多个字节,使用utf8.DecodeRune进行解码。
if r >= utf8.RuneSelf {
r, size = utf8.DecodeRune(b.buf[b.r:b.w])
}
// 更新读指针和记录最近读取的字节和rune的信息。
b.r += size
b.lastByte = int(b.buf[b.r-1])
b.lastRuneSize = size
return r, size, nil
}
解释:
ReadRune
方法是 Reader
结构体的方法,用于读取单个UTF-8编码的Unicode字符。utf8.RuneSelf
,则表示可能占用多个字节,使用utf8.DecodeRune
进行解码。作用:
ReadRune
方法用于从缓冲区中读取一个UTF-8编码的Unicode字符。fill
方法填充缓冲区。// UnreadRune 撤销最后读取的rune。如果最近在[Reader]上调用的方法不是[Reader.ReadRune],
// [Reader.UnreadRune]会返回错误(在这方面它比[Reader.UnreadByte]更严格,后者将从任何读取操作中撤销最后一个字节)。
func (b *Reader) UnreadRune() error {
// 如果最近的rune的大小小于零或者读指针小于rune的大小,返回无效的rune撤销错误。
if b.lastRuneSize < 0 || b.r < b.lastRuneSize {
return ErrInvalidUnreadRune
}
// 撤销rune,更新读指针和记录最近读取的字节和rune的信息。
b.r -= b.lastRuneSize
b.lastByte = -1
b.lastRuneSize = -1
return nil
}
解释:
UnreadRune
方法是 Reader
结构体的方法,用于撤销最后读取的rune。作用:
UnreadRune
方法用于在不改变读取状态的情况下撤销最后读取的rune。// Buffered 返回当前缓冲区中可读取的字节数。
func (b *Reader) Buffered() int { return b.w - b.r }
// ReadSlice 读取直到输入中第一个定界符的位置,返回一个指向缓冲区中字节的切片。
// 这些字节在下一次读取时将不再有效。
// 如果 ReadSlice 在找到定界符之前遇到错误,
// 它将返回缓冲区中的所有数据和错误本身(通常是 io.EOF)。
// 如果缓冲区在没有定界符的情况下填满,ReadSlice 失败,返回错误 ErrBufferFull。
// 由于从 ReadSlice 返回的数据将被下一次 I/O 操作覆盖,
// 大多数客户端应该使用 [Reader.ReadBytes] 或 ReadString 来代替。
// 如果 line 不以定界符结束,ReadSlice 返回 err != nil。
func (b *Reader) ReadSlice(delim byte) (line []byte, err error) {
s := 0 // 搜索开始索引
for {
// 在缓冲区中搜索定界符。
if i := bytes.IndexByte(b.buf[b.r+s:b.w], delim); i >= 0 {
i += s
line = b.buf[b.r : b.r+i+1]
b.r += i + 1
break
}
// 有挂起的错误吗?
if b.err != nil {
line = b.buf[b.r:b.w]
b.r = b.w
err = b.readErr()
break
}
// 缓冲区是否已满?
if b.Buffered() >= len(b.buf) {
b.r = b.w
line = b.buf
err = ErrBufferFull
break
}
s = b.w - b.r // 不要重新扫描之前已经扫描过的区域
b.fill() // 缓冲区未满时进行填充
}
// 处理最后一个字节(如果有的话)。
if i := len(line) - 1; i >= 0 {
b.lastByte = int(line[i])
b.lastRuneSize = -1
}
return
}
解释:
ReadSlice
方法是 Reader
结构体的方法,用于从缓冲区中读取直到指定定界符的切片。s
为零。line
指向缓冲区中的字节,并更新读指针 b.r
。作用:
ReadSlice
方法用于从缓冲区中读取直到指定定界符的切片。这部分代码定义了 ReadLine
方法,用于低级别的读取一行数据。大多数调用者应该使用 Reader.ReadBytes('\n')
或 Reader.ReadString('\n')
,或者使用 Scanner
。
// ReadLine 是一个低级别的读取一行数据的原语。大多数调用者应该使用
// [Reader.ReadBytes]('\n') 或 [Reader.ReadString]('\n'),或者使用 [Scanner]。
//
// ReadLine 试图返回一行数据,不包括行尾的字节。
// 如果行太长无法容纳在缓冲区中,则 isPrefix 被设置为 true,返回行的开头。
// 行的其余部分将在将来的调用中返回。当返回行的最后一部分时,isPrefix 将为 false。
// ReadLine 要么返回一个非空的行,要么返回一个错误,而不会同时返回两者。
//
// 从 ReadLine 返回的文本不包括行尾 ("\r\n" 或 "\n")。
// 如果输入在没有最终行尾的情况下结束,将不会给出指示或错误。
// 在调用 ReadLine 后调用 [Reader.UnreadByte] 将始终取消读取最后一个字节
// (可能是属于行尾的字符),即使该字节不是 ReadLine 返回的行的一部分。
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) {
line, err = b.ReadSlice('\n')
if err == ErrBufferFull {
// 处理 "\r\n" 横跨缓冲区的情况。
if len(line) > 0 && line[len(line)-1] == '\r' {
// 将 '\r' 放回缓冲区并从 line 中删除。
// 让下一次调用 ReadLine 检查 "\r\n"。
if b.r == 0 {
// 不应该达到的地方
panic("bufio: tried to rewind past start of buffer")
}
b.r--
line = line[:len(line)-1]
}
return line, true, nil
}
if len(line) == 0 {
if err != nil {
line = nil
}
return
}
err = nil
if line[len(line)-1] == '\n' {
drop := 1
if len(line) > 1 && line[len(line)-2] == '\r' {
drop = 2
}
line = line[:len(line)-drop]
}
return
}
解释:
ReadLine
方法是 Reader
结构体的方法,用于读取一行数据。ReadSlice('\n')
方法读取一行数据,返回的 line
是不包括行尾的。\r
放回缓冲区,同时从 line
中删除。line
置为 nil
。\n
,则删除 \n
及其前面可能的 \r
。line
,是否为行的前缀 isPrefix
,以及可能存在的错误 err
。作用:
ReadLine
方法提供了一个低级别的接口,用于读取一行数据。ReadSlice
实现行的读取,并处理了行尾跨越缓冲区的情况。line
不包括行尾的字节,isPrefix
指示是否还有剩余部分未读取,err
表示是否有错误发生。这部分代码定义了 collectFragments
方法,用于读取输入直到第一次出现分隔符 delim
。它返回一个结果元组:(完整缓冲区的切片,分隔符之前的剩余字节,前两个元素的总字节数,错误)。
完整的结果等同于 bytes.Join(append(fullBuffers, finalFragment), nil)
,其长度为 totalLen
。为了允许调用者最小化分配和复制,结果被结构化成这样。
func (b *Reader) collectFragments(delim byte) (fullBuffers [][]byte, finalFragment []byte, totalLen int, err error) {
var frag []byte
// 使用 ReadSlice 查找 delim,累积完整缓冲区。
for {
var e error
frag, e = b.ReadSlice(delim)
if e == nil { // 获取最后一个片段
break
}
if e != ErrBufferFull { // 意外的错误
err = e
break
}
// 复制缓冲区。
buf := bytes.Clone(frag)
fullBuffers = append(fullBuffers, buf)
totalLen += len(buf)
}
totalLen += len(frag)
return fullBuffers, frag, totalLen, err
}
解释:
collectFragments
方法是 Reader
结构体的方法,用于从输入中读取数据,直到第一次出现分隔符 delim
。frag
用于保存每次读取的片段。ReadSlice
方法循环查找分隔符 delim
,同时累积完整的缓冲区。
ErrBufferFull
,表示发生了意外错误,将错误存储在 err
中,跳出循环。ErrBufferFull
,表示缓冲区已满,将缓冲区的副本添加到 fullBuffers
中,并累加总字节数。frag
的长度累加到总字节数。fullBuffers
,最后一个片段 frag
,前两个元素的总字节数 totalLen
,以及可能存在的错误 err
。作用:
collectFragments
方法提供了一个在读取数据时累积完整缓冲区的机制。ReadSlice
方法查找分隔符 delim
,并在发现分隔符之前累积完整的缓冲区。这部分代码定义了 ReadBytes
方法,用于读取输入直到第一次出现分隔符 delim
,返回包含数据和分隔符在内的切片。如果在找到分隔符之前遇到错误,它将返回错误之前读取的数据和错误本身(通常是 io.EOF)。ReadBytes
只在返回的数据不以分隔符结束时才返回 err != nil
。对于简单的用途,可能更方便使用 Scanner。
func (b *Reader) ReadBytes(delim byte) ([]byte, error) {
// 使用 collectFragments 读取数据直到分隔符。
full, frag, n, err := b.collectFragments(delim)
// 为了容纳完整的片段和碎片,分配新的缓冲区。
buf := make([]byte, n)
n = 0
// 将完整的片段和碎片复制到新的缓冲区中。
for i := range full {
n += copy(buf[n:], full[i])
}
copy(buf[n:], frag)
return buf, err
}
解释:
ReadBytes
方法是 Reader
结构体的方法,用于读取输入直到第一次出现分隔符 delim
。collectFragments
方法获取完整缓冲区的切片 full
、最后一个片段 frag
、前两个元素的总字节数 n
和可能存在的错误 err
。buf
,长度为 n
。buf
和可能存在的错误 err
。作用:
ReadBytes
方法提供了一种读取数据直到特定分隔符的方式,并返回包含分隔符在内的切片。collectFragments
方法实现了从输入中读取数据的过程。这部分代码定义了 ReadString
方法,用于读取输入直到第一次出现分隔符 delim
,返回包含数据和分隔符在内的字符串。如果在找到分隔符之前遇到错误,它将返回错误之前读取的数据和错误本身(通常是 io.EOF)。ReadString
只在返回的数据不以分隔符结束时才返回 err != nil
。对于简单的用途,可能更方便使用 Scanner。
func (b *Reader) ReadString(delim byte) (string, error) {
// 使用 collectFragments 读取数据直到分隔符。
full, frag, n, err := b.collectFragments(delim)
// 为了容纳完整的片段和碎片,分配新的字符串构建器。
var buf strings.Builder
buf.Grow(n)
// 将完整的片段和碎片写入字符串构建器。
for _, fb := range full {
buf.Write(fb)
}
buf.Write(frag)
return buf.String(), err
}
解释:
ReadString
方法是 Reader
结构体的方法,用于读取输入直到第一次出现分隔符 delim
。collectFragments
方法获取完整缓冲区的切片 full
、最后一个片段 frag
、前两个元素的总字节数 n
和可能存在的错误 err
。buf
,并预先分配足够的空间。err
。作用:
ReadString
方法提供了一种读取数据直到特定分隔符的方式,并返回包含分隔符在内的字符串。collectFragments
方法实现了从输入中读取数据的过程。