Go bufio.Scanner 处理空行与扫描错误
用 bufio.Scanner 按行读取文本时,空行不是“没有读到数据”:默认的 bufio.ScanLines 会把它作为一个长度为 0 的 token 返回。真正的扫描错误也不会直接从 Scan() 返回错误,而是要在循环结束后调用 Err() 判断。
因此,稳定的写法是先决定空行是否有业务意义,再在 for scanner.Scan() 结束后检查 scanner.Err()。如果输入可能包含很长的一行,还要在第一次调用 Scan() 之前配置 Buffer,否则默认缓冲区可能导致扫描提前停止。
官方文档:https://pkg.go.dev/bufio
先确认空行到底会返回什么
NewScanner 默认使用 ScanLines。它会去掉行尾的换行符,但不会把中间的空行自动删除,所以输入 alpha\n\n beta 时,三次扫描得到的 token 依次是 alpha、空字符串和 beta。空行要不要忽略,是调用方的选择。

如果空行代表记录之间的分隔符,应该保留它;如果只是日志中的视觉留白,可以在拿到 token 后再跳过。不要用“扫描不到文本就结束”的判断替代 Scan(),因为这会把合法空行和真正的输入结束混在一起。
package main
import (
"bufio"
"fmt"
"strings"
)
func main() {
// 中间包含一个空行,用来观察 ScanLines 的默认行为。
input := "alpha\n\n beta\n"
scanner := bufio.NewScanner(strings.NewReader(input))
for scanner.Scan() {
line := scanner.Text()
// 空行仍然是一次成功扫描;是否跳过由业务规则决定。
if line == "" {
fmt.Println("发现空行")
continue
}
fmt.Printf("保留内容:%q\n", line)
}
// Scan 返回 false 后再检查错误,正常 EOF 不会被当成错误。
if err := scanner.Err(); err != nil {
fmt.Printf("扫描失败:%v\n", err)
}
}
这里用 Text() 把最近一次 token 转成字符串,适合需要长期保存或继续处理的文本。若只需要短暂查看字节,可以使用 Bytes(),但它指向的底层数组可能在下一次 Scan() 后被覆盖。
把空行策略写成明确的业务分支
常见的旧代码会直接把每一行追加到切片,结果把配置文件中的空行也当成有效记录;另一种代码使用 strings.TrimSpace 后再判断,这又会把只含空格或制表符的行一并删除。两种行为都可能是对的,关键是不要无意中改变输入语义。
需要“只跳过真正的空行”时,用 line == "";需要“忽略只含空白字符的行”时,才使用 strings.TrimSpace(line) == ""。如果缩进本身有意义,不要在保存前无条件 Trim。
func collectRecords(scanner *bufio.Scanner, ignoreBlankOnly bool) ([]string, error) {
var records []string
for scanner.Scan() {
line := scanner.Text()
// 只在业务明确要求时跳过空白行,避免误删缩进信息。
if ignoreBlankOnly && strings.TrimSpace(line) == "" {
continue
}
records = append(records, line)
}
// 循环结束后统一返回扫描错误,调用方可以决定是否重试或终止。
if err := scanner.Err(); err != nil {
return nil, err
}
return records, nil
}
扫描结束后一定要检查 Err
Scan() 的返回值只有一个布尔值:true 表示拿到了下一个 token,false 表示没有下一个 token。它既可能是正常读到 EOF,也可能是底层 Reader 返回错误,或者 token 超过了当前允许的最大长度。因此只写循环而不检查 Err(),就会把“读取了一半”误认为“文件完整读完”。

func readLines(r io.Reader) ([]string, error) {
scanner := bufio.NewScanner(r)
var lines []string
for scanner.Scan() {
// Text 只取当前 token;需要保留空行时不要过滤空字符串。
lines = append(lines, scanner.Text())
}
// Err 会忽略 io.EOF,但会返回其他读取错误或 token 过长错误。
if err := scanner.Err(); err != nil {
return nil, err
}
return lines, nil
}
如果输入来自文件,文件本身仍然要由调用方负责关闭;Scanner 不会替你关闭传入的 io.Reader。推荐在打开文件成功后立即注册 defer Close(),并把扫描错误与关闭错误按项目规范处理。
长行导致扫描失败时配置 Buffer
Scanner 默认有内部 token 缓冲上限。遇到单行 JSON、堆栈信息或导出字段特别长的输入时,Scan() 可能返回 false,随后 Err() 报告 token 过长。此时应在第一次调用 Scan() 前调用 Buffer。
func scanLongLines(r io.Reader) error {
scanner := bufio.NewScanner(r)
// 初始缓冲区减少小幅扩容;最大值要覆盖业务允许的单行长度。
scanner.Buffer(make([]byte, 64*1024), 2*1024*1024)
for scanner.Scan() {
// 这里只处理当前行,避免把所有内容一次性堆进内存。
line := scanner.Text()
_ = line
}
// Buffer 不能替代错误检查,超出最大值仍会从 Err 暴露出来。
return scanner.Err()
}
Buffer 的第二个参数是允许分配的最大 token 尺寸,不是“文件最大尺寸”。即使把它调大,也不代表适合用 Scanner 处理无界输入;如果必须控制读取边界、恢复读取过程或顺序执行多轮扫描,应评估 bufio.Reader 等更可控的方案。
按这份清单迁移旧的按行读取代码
| 检查项 | 迁移后的明确行为 |
|---|---|
| 空行 | 明确保留,或在 token 已返回后按规则跳过 |
| 只含空格的行 | 只有业务允许时才使用 TrimSpace 判断 |
| 扫描结束 | 循环后调用 Err,不能只看 Scan 返回 false |
| 超长 token | 在第一次 Scan 前设置 Buffer,并限制最大值 |
| 文件资源 | 由打开文件的一方负责 Close,Scanner 不接管生命周期 |
| 末尾无换行 | ScanLines 仍会返回最后一个非空行 |
回归测试至少覆盖:连续两个换行符、只有空白字符的行、最后一行没有换行符、Reader 返回错误,以及超过 Buffer 上限的单行。这样才能确认“空行处理”没有掩盖“输入未完整读取”的问题。
常见问题
空行会让 Scanner 提前结束吗?
不会。默认 ScanLines 会返回空 token,下一次扫描仍会继续读取后面的内容。
为什么 Scan 返回 false 但 Err 是 nil?
通常是正常到达 EOF。Err() 只报告非 EOF 错误,所以仍然应该调用它来完成判断。
调用 Buffer 后还是报 token too long 怎么办?
确认 Buffer 在第一次 Scan() 前调用,并检查第二个参数是否覆盖真实单行长度;如果输入长度没有可靠上限,应重新评估读取器选择。
Scanner 会自动关闭文件吗?
不会。传入的 Reader 生命周期仍由调用方管理,文件读取完成后要按原有责任链关闭。
把空行、EOF、读取错误和超长 token 分成四种状态后,bufio.Scanner 的行为就比较稳定:循环负责消费 token,Err() 负责揭示失败原因,Buffer 负责把明确的单行上限传达给运行时。
Go bufio.Scanner 提高超长 token 读取上限
- 上一篇
- Go bufio.Scanner 提高超长 token 读取上限
- 下一篇
- Go io.Pipe 写端关闭后读端为何收到 EOF
-
- Golang · Go教程 | 20分钟前 | 标准库 · JSON · Go教程 · encoding/json · Go encoding/json bytes.Buffer json.Compact JSON压缩
- Go json.Compact 怎样去掉 JSON 空白并保留字符串内容
- 214浏览 收藏
-
- Golang · Go教程 | 22分钟前 | 标准库 · go · CGO · Go CGO runtime/asan AddressSanitizer C内存越界 go build -asan
- Go runtime/asan 怎样辅助排查 C 内存越界
- 304浏览 收藏
-
- Golang · Go教程 | 24分钟前 |
- Go json.Valid 怎样只检查 JSON 语法而不解析数据
- 203浏览 收藏
-
- Golang · Go教程 | 26分钟前 | 标准库 · JSON · Go教程 · encoding/json · Go encoding/json Prefix JSON格式化 indent json.MarshalIndent
- Go json.MarshalIndent 的 prefix 与 indent 输出层级控制
- 204浏览 收藏
-
- Golang · Go教程 | 29分钟前 | 标准库 · JSON · go · encoding/json · Go bytes.Buffer Prefix json.Indent JSON格式化 indent
- Go json.Indent 怎样把 JSON 格式化而不改动字符串内容
- 487浏览 收藏
-
- Golang · Go教程 | 31分钟前 |
- Go time.Duration 检测乘法溢出
- 392浏览 收藏
-
- Golang · Go教程 | 57分钟前 | 标准库 · csv · Go教程 · csv Go error FLUSH CRLF encoding/csv Writer.UseCRLF Windows换行
- Go encoding/csv Writer.UseCRLF 怎样输出 Windows 换行
- 103浏览 收藏
-
- Golang · Go教程 | 59分钟前 | 标准库 · 错误处理 · IO · Go教程 · Go eof io.Pipe CloseWithError PipeReader PipeWriter.Close
- Go io.Pipe 写端关闭后读端为何收到 EOF
- 321浏览 收藏
-
- Golang · Go教程 | 1小时前 | 标准库 · go · 文件读取 · Go bufio.Scanner 超长文本 Scanner.Buffer
- Go bufio.Scanner 提高超长 token 读取上限
- 365浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go io.SectionReader 在重试下载中复用读取边界
- 178浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go io.SectionReader 结合 Seek 定位分片
- 362浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go io.SectionReader 如何为并发分片读取复用底层 ReaderAt
- 154浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 809次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 798次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 890次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 862次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 687次使用
-
- GScript 编写标准库示例详解
- 2022-12-30 369浏览
-
- 关于Golang标准库flag的全面讲解
- 2023-02-25 344浏览
-
- Go代码规范错误处理示例经验总结
- 2022-12-23 278浏览
-
- Golang标准库unsafe源码解读
- 2022-12-29 464浏览
-
- 快速掌握Go语言HTTP标准库的实现方法
- 2022-12-30 327浏览

