Go bufio.Scanner 如何自定义分隔符读取记录
如果输入不是按换行分隔,而是用 ||、@@ 这类业务标记分隔记录,Go 仍然可以继续使用 bufio.Scanner。做法是给 Scanner 注册一个自定义 SplitFunc:找到完整分隔符就返回一条 token,暂时找不到就等待更多字节,读到 EOF 再把末尾余量交出来。
Scanner.Split接收的是函数,不是单个字符;多字节分隔符可用bytes.Index查找。- 没有完整记录时必须返回
0, nil, nil;atEOF时要处理没有尾部标记的最后一条记录。 - 默认 token 缓冲上限是 64 KiB,长记录先用
Buffer调整,仍需要更强控制时考虑bufio.Reader。
自定义 SplitFunc 要同时处理分隔符和 EOF
Scanner 的默认切分规则是按行读取;Split 可以把它替换为业务规则。一个 SplitFunc 每次面对当前字节片段时,需要返回“应跳过多少字节”“本次 token 是什么”和“是否出错”三个结果。分隔符还没收完整时,不能急着把片段当成记录。

下面的函数支持多字节标记,并允许输入末尾没有 ||:
package main
import "bytes"
// splitByMark 按完整字节分隔符切分记录;分隔符本身不会进入 token。
func splitByMark(mark []byte) func([]byte, bool) (int, []byte, error) {
return func(data []byte, atEOF bool) (int, []byte, error) {
if i := bytes.Index(data, mark); i >= 0 {
// advance 必须越过整个分隔符,避免下一次扫描重复看到它。
return i + len(mark), data[:i], nil
}
if atEOF && len(data) > 0 {
// 最后一条记录没有结束标记时,仍然把剩余字节交给调用方。
return len(data), data, nil
}
// 分隔符可能被拆在两次读取之间,先保留当前数据等待补齐。
return 0, nil, nil
}
}
这里的 advance 是字节数,不是字符数,所以要写成 i + len(mark)。如果返回 0, nil, nil,Scanner 会继续向底层 Reader 请求数据;若直接返回当前片段,跨读取边界的分隔符就可能被误判。
给 Scanner 配置切分规则,再检查 Err
配置顺序很简单:先创建 Scanner,再设置 Split 和 Buffer,最后进入 Scan 循环。Buffer 必须在扫描开始前调用,max 表示单个 token 允许达到的最大缓冲范围。
package main
import (
"bufio"
"fmt"
"strings"
)
// readRecords 演示按 || 读取订单号,并在循环结束后区分 EOF 与真正错误。
func readRecords(input string) error {
scanner := bufio.NewScanner(strings.NewReader(input))
scanner.Split(splitByMark([]byte("||")))
// 允许单条记录最多约 1 MiB;初始缓冲只按需分配。
scanner.Buffer(make([]byte, 1024), 1024*1024)
for scanner.Scan() {
// Text 在本轮扫描内转成字符串;需要跨轮保存时应复制数据。
fmt.Printf("record=%q\n", scanner.Text())
}
if err := scanner.Err(); err != nil {
return fmt.Errorf("scan records: %w", err)
}
return nil
}
例如输入 order-1001||order-1002||order-1003,循环会得到三条记录,即使最后没有额外的 ||。Scanner 的正常结束不会把 EOF 当成错误;但底层读取失败或 token 超过上限,会在 Err() 中暴露。
空记录、token 生命周期和缓冲区上限不能混为一谈
连续分隔符会产生空 token。例如 A||||B 中间存在一条空记录。如果业务不允许空记录,要在循环里显式跳过或报错,不要在 SplitFunc 里悄悄改变记录语义。
Scanner.Bytes() 返回当前 token 的字节切片,下一次扫描后不要继续依赖它;需要放入队列、异步处理或长期保存时,复制一份。Scanner.Text() 适合直接得到字符串,但同样不应把“本轮 token”当成永久缓冲。
官方文档给出的默认最大 token 缓冲是 64 KiB,实际还可能需要容纳分隔符。可以用 Buffer 提高上限,但上限越大,异常输入占用的内存边界也越宽:
| 现象 | 处理方式 | 判断依据 |
|---|---|---|
| 记录短、边界固定 | 直接用 Scanner | 自定义 SplitFunc 足够表达规则 |
| 偶发长记录 | 先调用 Buffer | 可接受明确的单条记录上限 |
| 记录可能极长或需分段恢复 | 改用 bufio.Reader | 需要更强的错误控制和读取过程控制 |

什么时候不该继续扩大 Scanner 的 max
Buffer 解决的是“已知上限内的长 token”,不是无限长度输入的通用方案。如果输入来自不可信网络、单条记录可能持续增长,或者程序需要在超长数据中逐段恢复,继续把 max 调到很大只会把风险推迟。此时用 bufio.Reader.ReadString、ReadBytes 或更细粒度的读取逻辑,通常更容易控制内存和错误路径。
落地前可以按这份清单判断:分隔符是否可能跨读取边界;末尾无分隔符是否仍算有效记录;连续分隔符是否允许空记录;记录长度上限是多少;token 是否会交给异步任务;扫描结束后是否检查了 Err()。这些问题都明确后,自定义 Scanner 就会从一个小技巧变成稳定的输入边界。
常见问题
SplitFunc 能按中文字符串分隔吗?
可以。Scanner 面向字节,中文分隔符应使用 UTF-8 编码后的 []byte,例如 []byte("结束");不要按 rune 下标去计算 advance。
为什么最后一条记录读不到?
通常是 SplitFunc 只查找分隔符,没有在 atEOF 且仍有数据时返回余量。补上 EOF 分支即可读取没有尾部分隔符的记录。
Scanner 报 token too long 怎么办?
如果记录长度有可靠上限,在首次 Scan 前调用 Buffer 设置更大的 max;如果长度不可控或需要分段恢复,应改用 bufio.Reader。
LiblibAI SD在线绘图如何把草图生成成品?图生图操作步骤
- 上一篇
- LiblibAI SD在线绘图如何把草图生成成品?图生图操作步骤
- 下一篇
- Go JSON 数字进 interface 后为什么变成 float64
-
- Golang · Go教程 | 11分钟前 | 错误处理 · go · 换行符 · 文件导出 · encoding/csv · Go FLUSH CSV导出 csv.Writer UseCRLF
- Go csv.Writer 如何保证导出文件末尾换行一致
- 145浏览 收藏
-
- Golang · Go教程 | 19分钟前 | 文件读取 · csv · Go教程 · ParseError · Go CSV解析 encoding/csv 多行字段
- Go encoding/csv 如何读取带换行的引号字段
- 203浏览 收藏
-
- Golang · Go教程 | 55分钟前 | go · bufio · io.Reader · peek bufio.Reader Go预读
- Go bufio.Reader 如何查看下一行但不消费内容
- 150浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go os.CreateTemp 如何按业务前缀生成临时文件
- 117浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go embed.FS 如何读取嵌入文件的相对路径
- 195浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go fs.WalkDir 如何在遇到权限错误时保留其他目录
- 253浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go io.MultiWriter 如何同时写文件和摘要哈希
- 215浏览 收藏
-
- Golang · Go教程 | 2小时前 | golang · io.Reader · EOF · 流式读取 · 字节限制 · Go io.Reader io.LimitReader 读取上限 LimitedReader
- Go io.Reader 如何限制单次读取的最大字节数
- 299浏览 收藏
-
- Golang · Go教程 | 2小时前 | 字符串 · 标准库 · Go教程 · 并发边界 · 内存语义 · Go string 字符串拼接 strings.Builder strings.Clone
- Go strings.Builder 写入后如何避免返回字符串被意外修改
- 236浏览 收藏
-
- Golang · Go教程 | 2小时前 | go · 二进制 · bytes.Buffer · bytes.Buffer Go二进制拼接 Go缓冲区复用
- Go bytes.Buffer 如何复用来拼接多段二进制数据
- 396浏览 收藏
-
- 前端进阶之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测试功能,助您快速选择最适合项目的高性能大语言模型。
- 98次使用
-
- OpenCompass
- OpenCompass是上海AI实验室推出的开源大模型评测平台,提供CompassKit、CompassHub和CompassRank三大核心组件,支持LLM及多模态模型的一站式标准化评估与排行榜查询。
- 28次使用
-
- SuperCLUE
- SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
- 252次使用
-
- C-Eval
- 深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
- 180次使用
-
- AI Prompt Library
- 探索AI Prompt Library免费资源库,涵盖营销、写作及多场景AI提示词。兼容ChatGPT、Claude等工具,一键复制优化输出,提升工作效率。
- 113次使用
-
- 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浏览
