Go源码学习:bufio包 - 1.1 - bufio.go -(2)
bufio包学习的上一篇文章:Go源码学习:bufio包 - 1.1 - bufio.go -(1)
7、Read:读取数据
// 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
中读取数据。 - 该方法返回读取的字节数以及可能出现的错误。
8、ReadByte:读取单个字节
// 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
方法用于从缓冲区中读取一个字节。- 如果缓冲区为空,会先进行填充。
- 返回读取到的字节和可能的错误。
9、UnreadByte:撤销最近读取的字节
// 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
结构体的方法,用于撤销最近读取的字节。- 首先,检查最近的字节值是否为负值(表示未读取),或者读指针为0但写指针大于0(表示有未读取的数据),如果是,则返回无效的撤销字节错误。
- 如果读指针大于0,表示有已读取的数据,将读指针前移一位;否则,如果读指针为0且写指针为0,表示缓冲区为空,将写指针设置为1。
- 将最近读取的字节值写回缓冲区的读指针位置。
- 最后,重置最近读取的字节和最近 rune 大小的状态,并返回 nil 表示成功。
作用:
UnreadByte
方法允许撤销最近成功读取的字节,将读指针前移,使得该字节重新可用。- 这个方法在需要回退一个字节时非常有用,例如在某些解析器中解析时需要回退到之前的状态。
10、ReadRune:读取单个UTF-8编码的Unicode字符
// 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字符。- 首先,通过循环确保缓冲区中有足够的数据以确保完整的rune被读取。
- 如果缓冲区为空,返回读取错误。
- 读取一个rune,并设置默认大小为1。
- 如果rune的值大于或等于
utf8.RuneSelf
,则表示可能占用多个字节,使用utf8.DecodeRune
进行解码。 - 更新读指针和记录最近读取的字节和rune的信息。
- 返回读取的rune、大小和nil表示无错误。
作用:
ReadRune
方法用于从缓冲区中读取一个UTF-8编码的Unicode字符。- 如果缓冲区中没有足够的数据以确保完整的rune被读取,会调用
fill
方法填充缓冲区。 - 方法还处理了可能的错误情况,例如缓冲区为空或编码的字符无效。
11、UnreadRune:撤销最后读取的rune
// 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。- 首先,检查最近的rune的大小是否小于零或者读指针是否小于rune的大小,如果是,返回无效的rune撤销错误。
- 然后,撤销rune,更新读指针和记录最近读取的字节和rune的信息。
- 返回nil表示无错误。
作用:
UnreadRune
方法用于在不改变读取状态的情况下撤销最后读取的rune。- 它会检查是否满足条件进行撤销,如果不满足条件则返回错误。
- 撤销操作将更新读取指针和记录的最近读取的字节和rune的信息。
12、ReadSlice:读取直到定界符的切片
// 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
。 - 如果存在挂起的错误,返回缓冲区中的所有数据和错误本身,并更新读指针。
- 如果缓冲区填满而没有找到定界符,返回缓冲区和错误 ErrBufferFull。
- 如果以上条件都不满足,填充缓冲区并继续搜索。
- 如果找到定界符,构建切片
- 处理最后一个字节,更新记录的最后一个字节和rune的信息。
- 返回读取的切片和可能的错误。
作用:
ReadSlice
方法用于从缓冲区中读取直到指定定界符的切片。- 它提供了一种方便的方式来处理包含定界符的数据。
- 注意,由于返回的数据会被下一次 I/O 操作覆盖,大多数客户端应该使用 [Reader.ReadBytes] 或 ReadString 来代替。
13、ReadLine:读取一行数据
这部分代码定义了 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
表示是否有错误发生。
14、collectFragments:收集片段
这部分代码定义了 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
,并在发现分隔符之前累积完整的缓冲区。 - 返回的结果允许调用者最小化内存分配和数据复制。
15、ReadBytes:读取字节
这部分代码定义了 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
方法实现了从输入中读取数据的过程。 - 为了方便使用,将完整的片段和最后一个片段合并到一个新的缓冲区中返回。
16、ReadString:读取字符串
这部分代码定义了 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
方法实现了从输入中读取数据的过程。 - 为了方便使用,将完整的片段和最后一个片段合并到一个新的字符串中返回。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!