Go bufio.Scanner 读取超长行怎么安全扩容
用 bufio.Scanner 按行读日志时,短行一切正常,换成一条几十万字节的 JSON 日志却可能直接停止,并在 Scanner.Err() 中看到 bufio.Scanner: token too long。安全的处理方式不是把限制改成一个特别大的数字,而是在第一次 Scan() 前用 Buffer 设定“初始容量 + 最大 token 大小”,再根据业务允许的单行字节数决定是否继续使用 Scanner。
- Scanner 默认最大 token 约为 64 KiB,超长时 Scan 返回 false,必须检查 Err。
- Buffer 只能在第一次 Scan 前调用,max 要覆盖内容和换行符,并保留业务上限。
- 行长不可控、需要分段或要从超长行中恢复时,优先比较 bufio.Reader。
先把 Scanner 的上限和内存边界分开看
NewScanner 默认使用 ScanLines,它把一行作为一个 token 返回。默认缓冲上限是 64 KiB,而且真实可容纳的 token 可能略小,因为输入还需要放下换行符。超出边界后,扫描会不可恢复地停止;这不是“少读了一段”,而是当前 Scanner 已经不能继续完成这次按行扫描。
可以把边界理解成一条静态关系:输入流交给 Scanner,Scanner 通过 ScanLines 找行,token 先放进初始缓冲区;如果不够,缓冲区在最大 token 上限内扩容,超过上限才会落到 ErrTooLong。下面的图只展示这些组件之间的关系,便于先确定应该调哪一个参数。

用 Buffer 在第一次 Scan 前设置可控上限
最小改动是在创建 Scanner 后立即调用 Buffer。初始容量决定一开始准备多少空间,max 决定扫描期间允许申请到多大;二者不是同一个概念。示例把业务单行限制设为 2 MiB,并为 LF 或 CRLF 留出少量余量:
package main
import (
"bufio"
"fmt"
"io"
)
func readLines(r io.Reader) error {
scanner := bufio.NewScanner(r)
// 初始空间保持适中,最大值按业务允许的单行字节数设置。
const maxLineBytes = 2 * 1024 * 1024
// ScanLines 还需要容纳行尾分隔符,留出两个字节余量。
scanner.Buffer(make([]byte, 64*1024), maxLineBytes+2)
for scanner.Scan() {
line := scanner.Text()
// 这里处理一整行;不要把超大行无条件长期放入内存缓存。
_ = line
}
// Scan 返回 false 既可能是 EOF,也可能是 token 太长或底层读错误。
if err := scanner.Err(); err != nil {
return fmt.Errorf("读取文本行失败: %w", err)
}
return nil
}
顺序很重要:Buffer 在扫描开始后调用会 panic。循环结束也不能只看 Scan() 的 false,必须读取 Err();这样 EOF 会被正常区分,超长 token 和底层 I/O 错误也不会被悄悄吞掉。
超长行不是越大越好:按字节预算控制内存
max 应该来自业务边界,而不是拍一个很大的数。日志行、单条 JSON 消息和导入记录可以分别设定上限;超过上限时返回明确错误,让调用方丢弃、转存或改走专门的大记录通道。中文字符数和字节数也不能混用,UTF-8 文本的内存预算应按字节估算。
| 场景 | 建议 | 原因 |
|---|---|---|
| 稳定的小型日志 | 保留默认 Scanner | 边界清晰,代码简单 |
| 已知最大行长 | Buffer(适中初始值, 上限+分隔符余量) | 突破默认值且限制内存 |
| 长度不可控或需恢复 | 比较 bufio.Reader | 可以分段读取并自行处理边界 |
还有一个容易忽略的边界:Scanner.Bytes() 返回的底层数组可能在下一次 Scan 时被覆盖;如果要把当前行交给异步任务或跨循环保存,应复制它,或者直接使用 Text() 接受字符串分配。
什么时候应该换用 bufio.Reader
Scanner 适合“每条记录有明确上限、超限就失败”的输入。若一行可能非常长,或者需要读到分隔符后再决定如何处理,bufio.Reader 更容易控制:可以使用 ReadString 或 ReadBytes,也可以用 ReadLine 分段接收并通过前缀信息拼接。代价是调用方要自己处理片段、EOF 和单行累计大小。
选择关系可以概括为:Scanner 连接 Scanner.Buffer 与固定 token 上限,Reader 则把读取拆成 ReadString、ReadLine 等更细的读取接口;ErrTooLong 是 Scanner 的失败边界,不应把它当成可从中间恢复的信号。

常见问题
Buffer 的 max 是字符数还是字节数?
它对应字节缓冲区大小,应按 UTF-8 输入的字节预算估算,不要直接把“字符数”当成 max。
把 max 设置成 int 的最大值可以吗?
不建议。这样失去输入保护,异常大行可能带来内存压力;应根据日志、消息或记录的业务上限设置。
Scanner 扫描结束后为什么一定要检查 Err?
因为 Scan 返回 false 既可能代表正常 EOF,也可能代表 token 太长或底层读取错误;Err 才能区分这些情况。
Redis Stream 消费组如何处理 Pending 列表里的超时消息
- 上一篇
- Redis Stream 消费组如何处理 Pending 列表里的超时消息
- 下一篇
- VS Code 怎么用 Profiles 隔离不同项目的扩展配置
-
- Golang · Go教程 | 1小时前 | go · database/sql · Rows.Scan ·
- Go 扫描未知列数结果时怎么动态分配接收变量
- 328浏览 收藏
-
- Golang · Go教程 | 1小时前 | Go教程 · database/sql · 数据库元数据 · SQL NULL · Go database/sql rows ColumnTypes ColumnType.Length
- Go RowsColumnTypes 返回长度为 nil 时怎么解释
- 128浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go database/sql 怎么读取查询结果的列类型信息
- 319浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go 证书主机名校验失败时应该查 SAN 还是 CN
- 272浏览 收藏
-
- Golang · Go教程 | 2小时前 | https · Go教程 · 证书 · tls crypto/x509 CertPool SystemCertPool RootCAs
- Go x509.SystemCertPool 返回空池时怎么兼容不同系统
- 327浏览 收藏
-
- Golang · Go教程 | 2小时前 |
- Go TLS 客户端连接内网证书时怎么追加自定义 CA
- 255浏览 收藏
-
- Golang · Go教程 | 2小时前 |
- Go CheckRedirect 返回错误时怎么读取最后一次响应
- 285浏览 收藏
-
- Golang · Go教程 | 3小时前 |
- Go 跟随重定向后 Authorization 为什么消失
- 212浏览 收藏
-
- Golang · Go教程 | 3小时前 |
- Go HTTP Client 禁止自动重定向时怎么保留原始响应
- 458浏览 收藏
-
- Golang · Go教程 | 3小时前 | Go教程 · 结构体标签 · encoding/xml · XML 序列化 · encoding/xml XMLName Go Marshal xml.Name XML 属性
- Go Marshal 输出 XML 时怎么控制根节点和属性
- 278浏览 收藏
-
- Golang · Go教程 | 3小时前 | go · xml · encoding/xml ·
- Go XML 中同名嵌套元素怎么映射成切片
- 380浏览 收藏
-
- Golang · Go教程 | 3小时前 | go · 文本处理 · XML解析 · encoding/xml · Go 命名空间 encoding/xml CharData XMLName
- Go encoding/xml 怎么用 CharData 读取混合文本而不丢空白
- 168浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 26次使用
-
- SuperCLUE
- SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
- 179次使用
-
- C-Eval
- 深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
- 116次使用
-
- AI Prompt Library
- 探索AI Prompt Library免费资源库,涵盖营销、写作及多场景AI提示词。兼容ChatGPT、Claude等工具,一键复制优化输出,提升工作效率。
- 41次使用
-
- Generrated
- Generrated汇集9300+张DALL·E生成图像及对应提示词,支持查看完整图集、对比DALL·E 2与3版本差异,是AI绘图新手学习Prompt设计与获取创作灵感的实用工具。
- 23次使用
-
- Go map 并发写 panic 怎么办:从共享 map 到可控写入路径
- 2026-06-30 123浏览
-
- go语言中的defer关键字
- 2023-02-17 150浏览
-
- Golang中Interface接口的三个特性
- 2023-01-07 394浏览
-
- go语言中函数与方法介绍
- 2023-01-07 297浏览
-
- go语言数据类型之字符串string
- 2022-12-30 321浏览

