登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go问答

Go bufio.Scanner 遇到 ErrFinalToken 什么时候停止

来源:17golang原创

时间:2026-10-05 17:19:44 116浏览 收藏

如果自定义的 bufio.SplitFunc 返回 bufio.ErrFinalToken,Scanner 会把它当作“正常结束信号”,而不是扫描错误。返回值里的 token 不为 nil 时,当前 token 仍会交给调用者;token 为 nil 时,则直接停止。循环结束后调用 scanner.Err(),结果应为 nil。

要点速览
  • ErrFinalToken 只由 SplitFunc 返回,用来提前结束扫描。
  • 非 nil token 表示“这是最后一个 token”,nil token 表示“不要再交付 token”。
  • Scan() 负责告诉调用者是否还有 token,Err() 负责报告真正的扫描错误。

ErrFinalToken 的停止语义不是普通错误

很多排查从 Scan() 返回 false 开始,但 false 只说明没有下一个 token,并不能单独说明原因。Scanner 可能读完输入,也可能遇到普通错误,还可能收到 ErrFinalToken。这三种情况要靠 Err() 和 SplitFunc 的返回值区分。

官方实现把 ErrFinalToken 作为特殊哨兵处理:它不会被保存成最终错误。实际判断可以记成一张小表:

SplitFunc 返回Scan 表现Err 表现
非 nil token + ErrFinalToken先交付这个最后 token,之后结束nil
nil token + ErrFinalToken立即结束,不再交付 tokennil
普通 error停止扫描返回该 error
Go bufio Scanner 中 SplitFunc、ErrFinalToken、最后 token 与 Scanner.Err 的静态关系说明图
图1:ErrFinalToken 返回边界说明图,查看最后 token 与无错误停止之间的关系。

用返回值判断 Scanner 是否真的结束

使用时不要把 ErrFinalToken 当成需要重试的异常,也不要只在循环里打印“扫描结束”。正确的排查顺序是:循环体处理已经交付的 token,循环退出后再检查 scanner.Err()。如果 Err 为 nil,说明是正常结束或到达 EOF;如果非 nil,才需要继续查 Reader 或 SplitFunc。

package main

import (
    "bufio"
    "fmt"
    "strings"
)

func stopAtMarker(data []byte, atEOF bool) (advance int, token []byte, err error) {
    // 找到分隔标记后交付前面的内容,并声明这是最后一个 token。
    if i := strings.Index(string(data), ""); i >= 0 {
        return i + len(""), data[:i], bufio.ErrFinalToken
    }
    if atEOF && len(data) > 0 {
        // 输入结束但仍有尾部内容时,正常交付最后一段。
        return len(data), data, bufio.ErrFinalToken
    }
    // 数据不完整,要求 Scanner 继续读取;不能在这里返回普通错误。
    return 0, nil, nil
}

func main() {
    scanner := bufio.NewScanner(strings.NewReader("alphaignored"))
    scanner.Split(stopAtMarker)
    for scanner.Scan() {
        // ErrFinalToken 交付的 token 仍会进入循环体。
        fmt.Println(scanner.Text())
    }
    // 正常的 ErrFinalToken 停止不会被当成扫描失败。
    fmt.Printf("err=%v\\n", scanner.Err())
}

这个示例的关键不是标记字符串本身,而是返回三元组的组合:advance 负责消费边界,token 决定是否交付当前结果,ErrFinalToken 决定交付后不再请求后续 token。若把 token 改成 nil,循环体不会收到最后一项。

Go bufio Scanner 输入缓冲区、SplitFunc、token、ErrFinalToken、Scan 与 Err 的判断关系图
图2:Scanner 停止判断关系图,查看 token、Scan 与 Err 的边界分工。

最容易误判的三个边界

  • 把 false 当成错误。 Scan 返回 false 后必须调用 Err,不能凭返回值单独下结论。
  • 把最后 token 丢掉。 返回非 nil token 与 ErrFinalToken 时,当前 token 仍然是有效数据,应在循环体处理。
  • 用普通 error 代替哨兵。 普通错误会出现在 Err 中;如果业务只是提前停止,使用 ErrFinalToken 能让调用方区分“正常截断”和“读取失败”。

相关问题

ErrFinalToken 会让 Scanner.Err 返回它吗?

不会。它是 bufio 约定的正常停止信号,循环结束后 Err 通常为 nil。

为什么返回 nil token 后循环体没有最后一项?

nil token 的含义是只停止扫描,不交付一个 token;需要保留空 token 时,应返回非 nil 的空切片或字符串对应的字节切片。

什么时候应该返回普通 error?

当输入格式非法、Reader 失败或 SplitFunc 无法继续处理时返回普通 error,让调用方通过 Scanner.Err 得到失败原因。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>