1、Scanner:扫描器
这部分代码定义了 Scanner
结构体,提供了一个方便的接口用于读取数据,例如文本文件中的换行分隔的文本行。连续调用 [Scanner.Scan] 方法将遍历文件的“标记”,跳过标记之间的字节。标记的规定由类型为 [SplitFunc] 的分割函数定义;默认的分割函数将输入拆分为带有去除行终止符的行。 [Scanner.Split] 函数在此包中定义,用于将文件扫描为行、字节、UTF-8 编码的符文和以空格分隔的单词。客户端还可以提供自定义的分割函数。
扫描器在 EOF、第一个 I/O 错误或标记过大无法适应 [Scanner.Buffer] 时会不可恢复地停止。当扫描停止时,读者可能已经超出最后一个标记的任意远。需要更多对错误处理或大标记的控制,或者必须在读者上运行顺序扫描的程序,应改用 [bufio.Reader]。
type Scanner struct {
r io.Reader // 客户端提供的读取器。
split SplitFunc // 用于拆分标记的函数。
maxTokenSize int // 标记的最大大小;测试时可修改。
token []byte // 分割函数返回的上一个标记。
buf []byte // 作为分割函数参数使用的缓冲区。
start int // 缓冲区中未处理的第一个字节。
end int // 缓冲区中的数据结束位置。
err error // 粘性错误。
empties int // 连续空标记的计数。
scanCalled bool // 已调用 Scan;缓冲区正在使用。
done bool // 扫描已完成。
}
解释:
Scanner
是一个结构体,包含了多个字段,用于存储扫描器的状态信息。r
字段是客户端提供的读取器,用于从其读取数据。split
字段是用于拆分标记的函数,类型为SplitFunc
。maxTokenSize
字段表示标记的最大大小,可以在测试时进行修改。token
字段存储上一个由分割函数返回的标记。buf
字段是作为参数传递给分割函数的缓冲区。start
和end
字段表示缓冲区中未处理的数据的起始和结束位置。err
字段用于存储扫描过程中遇到的错误。empties
字段表示连续空标记的计数。scanCalled
字段表示是否已调用过Scan
方法,用于标记缓冲区是否正在使用。done
字段表示扫描是否已完成。
作用:
Scanner
结构体提供了一种方便的接口,用于按照指定的分割规则从输入中提取标记。- 客户端可以通过设置不同的分割函数来定义标记的规则。
- 扫描器能够在遇到 EOF、第一个 I/O 错误或标记过大无法适应缓冲区时停止扫描。
- 提供了一些方法用于获取扫描器的状态和结果。
2、SplitFunc:分割函数
这部分代码定义了 SplitFunc
类型,是用于标记拆分的函数签名。分割函数的参数包括剩余未处理数据的初始子串和一个标志 atEOF
,该标志报告 [Reader] 是否没有更多数据提供。返回值是要提前输入的字节数,如果有的话,还有要返回给用户的下一个标记,以及一个错误(如果有的话)。
type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)
解释:
SplitFunc
是一个函数类型,定义了用于标记拆分的函数签名。- 函数接收两个参数:剩余未处理数据的初始子串
data
和一个标志atEOF
,该标志表示 [Reader] 是否没有更多数据提供。 - 返回值包括要提前输入的字节数
advance
,要返回给用户的下一个标记token
,以及可能的错误err
。
作用:
- 分割函数是用于定义如何拆分标记的用户提供的函数,可以根据不同的需求实现不同的标记拆分逻辑。
- 它允许用户定义如何从输入数据中提取有意义的标记,以供扫描器使用。
3、Errors:扫描器的错误
这部分代码定义了几个由 Scanner
返回的错误。
var (
ErrTooLong = errors.New("bufio.Scanner: token too long")
ErrNegativeAdvance = errors.New("bufio.Scanner: SplitFunc returns negative advance count")
ErrAdvanceTooFar = errors.New("bufio.Scanner: SplitFunc returns advance count beyond input")
ErrBadReadCount = errors.New("bufio.Scanner: Read returned impossible count")
)
解释:
ErrTooLong
表示标记过长的错误。ErrNegativeAdvance
表示分割函数返回负的提前计数的错误。ErrAdvanceTooFar
表示分割函数返回超出输入的提前计数的错误。ErrBadReadCount
表示读取返回不可能的计数的错误。
作用:
- 这些错误常常用于指示在扫描器操作中出现的问题,例如标记过大、提前计数错误等。
4、const:定义常量
这部分代码定义了两个常量:
const (
// MaxScanTokenSize 是用于缓冲标记的最大大小,
// 除非用户提供带有 [Scanner.Buffer] 的显式缓冲区。
// 实际的最大标记大小可能较小,因为缓冲区可能需要包括换行符等。
MaxScanTokenSize = 64 * 1024
startBufSize = 4096 // 用于缓冲区的初始分配大小。
)
解释:
MaxScanTokenSize
是用于缓冲标记的最大大小的常量。如果用户没有提供显式缓冲区,这个值将用于分配缓冲区。实际的最大标记大小可能会更小,因为缓冲区可能需要包括换行符等。startBufSize
是用于缓冲区初始分配的大小的常量,为 4096 字节。
作用:
- 这些常量定义了在扫描器(
Scanner
)中使用的一些大小限制,以确保有效地处理标记,并提供了一些初始缓冲区的大小。
5、NewScanner:创建新的扫描器
这部分代码定义了一个 NewScanner
函数,用于创建一个新的扫描器(Scanner
)实例,以从提供的读取器 r
中读取数据。默认情况下,分割函数(split
)设置为 ScanLines
,并且最大标记大小(maxTokenSize
)设置为 MaxScanTokenSize
。
// NewScanner 返回一个新的扫描器以从 r 中读取数据。
// 分割函数默认为 ScanLines。
func NewScanner(r io.Reader) *Scanner {
return &Scanner{
r: r,
split: ScanLines,
maxTokenSize: MaxScanTokenSize,
}
}
解释:
NewScanner
是一个函数,返回一个指向Scanner
结构体的指针。- 该函数接受一个
io.Reader
类型的参数r
,表示从哪里读取数据。 - 创建了一个新的
Scanner
实例,并初始化了其字段:r
字段被设置为传入的读取器r
。split
字段默认设置为ScanLines
,表示默认使用行分割函数。maxTokenSize
字段设置为MaxScanTokenSize
,表示默认的最大标记大小。
作用:
NewScanner
函数的主要作用是创建一个新的扫描器实例,并进行必要的初始化。- 用户可以通过调用此函数来获取一个可用于扫描数据的扫描器,而无需手动创建和配置实例。
6、Err:获取扫描器的错误
这部分代码定义了 Err
方法,用于获取扫描器(Scanner
)遇到的第一个非 EOF 错误。
// Err 返回扫描器遇到的第一个非 EOF 错误。
func (s *Scanner) Err() error {
// 如果错误为 EOF,则返回 nil。
if s.err == io.EOF {
return nil
}
// 否则,返回扫描器的错误。
return s.err
}
解释:
Err
方法是Scanner
结构体的方法,其接收者是指向扫描器实例的指针s
。- 方法内部首先检查扫描器的错误是否为 EOF,如果是,表示没有遇到非 EOF 错误,返回 nil。
- 否则,返回扫描器的错误。
作用:
Err
方法用于获取扫描器遇到的第一个非 EOF 错误,方便用户检查扫描过程中是否发生了错误。
7、Bytes:获取最近生成的标记
这部分代码定义了 Bytes
方法,用于获取由调用 Scanner.Scan
生成的最近的标记(token)。
// Bytes 返回由调用 Scanner.Scan 生成的最近的标记。
// 底层数组可能指向将被后续调用 Scan 覆盖的数据。它不进行分配。
func (s *Scanner) Bytes() []byte {
return s.token
}
解释:
Bytes
方法是Scanner
结构体的方法,其接收者是指向扫描器实例的指针s
。- 方法简单地返回最近由调用
Scanner.Scan
生成的标记的字节切片,即s.token
。
作用:
Bytes
方法用于获取最近生成的标记的字节切片,不进行额外的分配,但需要注意底层数组可能会被后续的Scan
调用覆盖。
8、Text:获取最近生成的标记的字符串表示
这部分代码定义了 Text
方法,用于获取由调用 Scanner.Scan
生成的最近标记的字符串表示形式。
// Text 返回由调用 Scanner.Scan 生成的最近的标记的字符串表示形式,
// 作为新分配的字符串持有其字节。
func (s *Scanner) Text() string {
return string(s.token)
}
解释:
Text
方法是Scanner
结构体的方法,其接收者是指向扫描器实例的指针s
。- 方法返回最近由调用
Scanner.Scan
生成的标记的字符串表示形式,通过将字节切片s.token
转换为新分配的字符串实现。
作用:
Text
方法用于获取最近生成的标记的字符串表示形式,返回一个新分配的字符串,方便用户处理字符串形式的标记。
9、ErrFinalToken:终结标记的特殊错误值
这部分代码定义了 ErrFinalToken
,它是一个特殊的哨兵错误值。其目的是由 Split
函数返回,以指示扫描应该在没有错误的情况下停止。如果伴随此错误返回的标记不为 nil,则该标记是最后一个标记。
// ErrFinalToken 是一个特殊的哨兵错误值。它用于由 Split 函数返回,指示扫描应该在没有错误的情况下停止。
// 如果伴随此错误返回的标记不为 nil,则该标记是最后一个标记。
//
// 该值用于在提前停止处理或在需要传递最终空标记(与 nil 标记不同)时很有用。
// 可以通过自定义错误值实现相同的行为,但在这里提供一个值更为整洁。
// 请参阅 emptyFinalToken 示例以了解此值的用法。
var ErrFinalToken = errors.New("final token")
解释:
ErrFinalToken
是一个变量,代表一个特殊的哨兵错误值。- 该错误值旨在由
Split
函数返回,以指示扫描应该在没有错误的情况下停止。 - 如果伴随此错误返回的标记不为 nil,则该标记被视为最后一个标记。
- 此值用于提前停止处理或在需要传递最终空标记(与 nil 标记不同)时很有用。
- 提供此值是为了使代码更整洁,而不是使用自定义错误值。
作用:
ErrFinalToken
的作用是在特定情况下,通过Split
函数返回一个特殊的错误值,以指示扫描应该停止。这对于处理最后一个标记或提前停止扫描非常有用。