Go源码学习:bufio包 - 1.2 - scan.go -(1)

bufio包官方文档

Go源码学习-索引目录

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 字段是作为参数传递给分割函数的缓冲区。
  • startend 字段表示缓冲区中未处理的数据的起始和结束位置。
  • 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 函数返回一个特殊的错误值,以指示扫描应该停止。这对于处理最后一个标记或提前停止扫描非常有用。

猜你喜欢

转载自blog.csdn.net/weixin_49015143/article/details/135266375
今日推荐