bufio.Scanner token too long 如何改用 Reader
遇到 bufio.Scanner: token too long,通常不是文件坏了,而是 Scanner 的分词模型不适合当前输入。它默认按 token 缓冲,标准库文档给出的默认最大扫描尺寸是 64 KiB,而且实际可用长度还要给分隔符留空间。单行日志、压缩后的 JSON 或导入字段超过这个边界时,继续调业务逻辑没有意义,应该改用 bufio.Reader。
- 需要完整的一行:优先用
ReadString('\n')或ReadBytes('\n')。 - 行长不可控:用
ReadLine识别isPrefix,分段消费或拼接。 - 最后一段没有换行并不等于异常;只有错误不是
io.EOF时才应失败。
你可以优先用Reader的Read方法分块读全量内容,也可以配合bufio.Reader的ReadLine、ReadBytes方法替代Scanner的扫描逻辑,完全避开Scanner内置64KB单token长度限制的问题。
先定位 Scanner 的 token 上限与错误边界
Scanner.Scan 返回 false 后,要先读取 Err。如果得到 token too long,说明当前 split 函数识别到的一个 token 放不进 Scanner 的缓冲区。默认的 ScanLines 把一行当成 token,所以一行特别长就会触发这个限制;底层文件仍可能可以继续读取。
scanner := bufio.NewScanner(r)
for scanner.Scan() {
// 这里处理一行;短输入时 Scanner 的代码更简洁。
handle(scanner.Text())
}
if err := scanner.Err(); err != nil {
// token too long 与底层读取错误都从这里暴露。
return fmt.Errorf("scan input: %w", err)
}
如果业务确实知道单行上限,也可以在第一次 Scan 前调用 Scanner.Buffer 放大上限。但这仍然把整行装入 token,无法解决真正的无界输入;标题中的改用 Reader,重点就是把“分隔符读取”和“token 大小限制”拆开。

用 Reader.ReadString 按分隔符读取完整内容
需要得到完整字符串、并且单行大小在业务可接受范围内时,ReadString 最直接。它会持续读取到指定分隔符;返回值可能同时带着内容和错误,所以最后一段没有换行时,要先处理非空内容,再判断错误是否只是 io.EOF。
func readLines(r io.Reader, handle func(string) error) error {
br := bufio.NewReader(r)
for {
line, err := br.ReadString('\n')
if len(line) > 0 {
// 去掉行尾换行;最后一行没有换行时仍保留其内容。
line = strings.TrimSuffix(line, "\n")
line = strings.TrimSuffix(line, "\r")
if callbackErr := handle(line); callbackErr != nil {
// 回调只执行一次,避免重试副作用。
return fmt.Errorf("handle line: %w", callbackErr)
}
}
if errors.Is(err, io.EOF) {
// EOF 只代表输入自然结束,不代表最后一行丢失。
return nil
}
if err != nil {
return fmt.Errorf("read line: %w", err)
}
}
}
上例的回调应只调用一次并保存返回值,生产代码可把它改成 callbackErr := handle(line),避免失败时重复执行副作用。若不想把字节转成字符串,ReadBytes('\n') 使用同样的 EOF 判断,但返回的是 []byte。
用 Reader.ReadLine 分段处理超长行
当单行可能达到几十 MiB,直接用 ReadString 会产生较大的瞬时内存。ReadLine 返回 line、isPrefix 和错误:isPrefix=true 表示这一段还没有遇到完整换行。可以把片段交给增量解析器,也可以只在确实需要完整字符串时用 bytes.Buffer 拼起来。
func readLongLines(r io.Reader, handle func([]byte) error) error {
br := bufio.NewReader(r)
var full bytes.Buffer
for {
part, prefix, err := br.ReadLine()
if err != nil {
if errors.Is(err, io.EOF) && full.Len() == 0 {
return nil
}
return fmt.Errorf("read long line: %w", err)
}
// Copy 片段,避免后续读取覆盖 Reader 内部缓冲区中的字节。
full.Write(part)
if prefix {
// 还未到换行,继续读取同一行的下一段。
continue
}
if err := handle(full.Bytes()); err != nil {
return fmt.Errorf("handle long line: %w", err)
}
full.Reset()
}
}
ReadLine 的片段来自 Reader 的内部缓冲,不能把返回的切片长期保存后再继续读;需要跨轮次保留时必须复制。若输入来自不可信上传,拼接前还应自行设置总长度上限,超过上限立即丢弃当前记录并返回错误。

按输入边界选择 API 并补上回归检查
| 场景 | 建议 API | 关键判断 |
|---|---|---|
| 普通短行、需要完整字符串 | ReadString | 处理 EOF 携带的最后一段 |
| 需要字节切片或自定义分隔符 | ReadBytes | 确认切片生命周期和内存峰值 |
| 行长不可控、可增量处理 | ReadLine | isPrefix 为 true 时不可提前提交 |
| 长度明确且始终较小 | Scanner.Buffer | 设置上限,不把它当成无界读取 |
迁移后至少覆盖三组输入:恰好带换行的长行、文件末尾没有换行的长行、分成多个 Reader 缓冲片段的长行。再加一组底层 Reader 主动返回错误,确认错误不会被 io.EOF 分支吞掉。这样改动的核心不是把 64 KiB 改成更大的数字,而是让读取策略匹配输入边界。
常见问题
只把 Scanner.Buffer 调到 10 MiB 可以吗?
可以用于有明确上限的受控输入,但它仍会把一个 token 放进内存;对无界或不可信单行,Reader 分段读取更稳妥。
ReadString 返回内容和 io.EOF 时要丢掉内容吗?
不要。只要返回内容非空,应先处理最后一段;io.EOF 只表示后面没有更多字节。
ReadLine 的 isPrefix 为 true 能直接交给业务吗?
只有业务明确支持分片协议时才可以。按行解析通常要继续收集片段,直到 isPrefix=false 再提交一条记录。
encoding/csv 遇到引号换行时的解析处理
- 上一篇
- encoding/csv 遇到引号换行时的解析处理
- 下一篇
- Go 字符串包含非法 UTF-8 时怎样定位来源
-
- Golang · Go问答 | 18分钟前 | Go问答 · 数据导入 · Go encoding/csv csv.Reader LazyQuotes ParseError 引号换行
- encoding/csv 遇到引号换行时的解析处理
- 307浏览 收藏
-
- Golang · Go问答 | 26分钟前 | Timer · Go问答 · Go 定时任务 截止时间 time.Ticker time.Timer 周期漂移
- Ticker 周期漂移时如何改成按截止时间调度
- 308浏览 收藏
-
- Golang · Go问答 | 26分钟前 | time · go · time.Parse ParseInLocation Go时区
- time.Parse 解析带中文时区为什么失败
- 222浏览 收藏
-
- Golang · Go问答 | 28分钟前 | Go问答 · encoding/json · 输入校验 · Go unmarshal encoding/json JSON重复键 JSON排查
- JSON 重复键解码时为什么只剩最后一个值?
- 439浏览 收藏
-
- Golang · Go问答 | 30分钟前 | 结构体 · api设计 · Go问答 · encoding/json · Go 结构体 dto encoding/json 未导出字段 json.Marshal MarshalJSON
- json.Marshal 为什么忽略未导出的字段
- 295浏览 收藏
-
- Golang · Go问答 | 34分钟前 |
- json.Number 如何避免大整数精度丢失
- 298浏览 收藏
-
- Golang · Go问答 | 35分钟前 | Go问答 · 接口排查 · encoding/json · JSON Go unmarshal encoding/json DisallowUnknownFields UnmarshalTypeError struct tag
- JSON 字段有值却解码为零值的排查清单
- 273浏览 收藏
-
- Golang · Go问答 | 39分钟前 | net/http · Go问答 · Go net/http http/2 ResponseController ResponseWriter Hijack
- ResponseWriter 在 HTTP/2 下为什么不能直接 Hijack
- 115浏览 收藏
-
- Golang · Go问答 | 40分钟前 |
- http.TimeoutHandler 超时后为何还会出现多次写入
- 368浏览 收藏
-
- Golang · Go问答 | 42分钟前 | 重定向 · net/http · HTTP客户端 · Go问答 · Go HTTP重定向 http.Client CheckRedirect 跨主机跳转
- Go http.Client CheckRedirect 处理跨域跳转的配置方法
- 445浏览 收藏
-
- Golang · Go问答 | 1小时前 | net/http · Go问答 · 接口排查 · cookie Go Cookiejar http.Client Set-Cookie AddCookie
- Cookie 设置成功但请求没有携带的原因
- 116浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 1010次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 1014次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 1139次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 1108次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 954次使用
-
- 用Nginx反向代理部署go写的网站。
- 2023-01-17 502浏览
-
- GoLand调式动态执行代码
- 2023-01-13 502浏览
-
- Go types.Selection.Type 与 Obj.Type 为什么不总是相同
- 2026-11-10 501浏览
-
- Go flag.FlagSet ContinueOnError 怎么捕获帮助输出和解析错误
- 2026-11-05 501浏览
-
- Go netip.ParseAddr 为什么拒绝带端口字符串
- 2026-11-03 501浏览

