text/scanner 不适合分词,它专为 go 源码词法分析设计,无法按自定义分隔符或语义切分;应改用 bufio.scanner 配 splitfunc,或专用分词库如 tiktoken-go、gojieba。

别用 text/scanner 做分词 —— 它不是为这个设计的,强行用会切错、漏字、还难调试。
text/scanner 是词法分析器,不是分词器
text/scanner 的目标是识别 Go 源码风格的 token:比如把 "a, b + 1" 拆成 IDENT、COMMA、ADD 等类型。它按语法边界走,不按空格、标点或自定义分隔符切。
常见误用现象:
- 传入中文或带 emoji 的文本,
Scan()直接卡住或返回ILLEGAL - 想按
"|||"切分日志行,结果只扫到第一个|||后就停了 - 调
TokenText()拿到的是带引号的字符串字面量(如"hello"),不是干净的hello
根本原因:text/scanner 的 Mode 默认跳过空白,且不提供“按任意字节序列切”的能力。它甚至不保证返回的 token 是用户语义上的“词”。
按分隔符流式分词,用 bufio.Scanner 配 SplitFunc
这才是真正对标 Python str.split("|||") 或 shell cut -d'|' 的做法。关键在自定义 SplitFunc,它必须正确处理两个边界:
-
atEOF == true:最后一段没分隔符,得整段返回 len(data) :缓冲区还没读满,不能贸然切,得返回 <code>0, false让 Scanner 继续读
一个最小可用的 "|||" 分隔实现:
func splitOnTriplePipe(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if i := bytes.Index(data, []byte("|||")); i >= 0 {
return i + 3, data[0:i], nil
}
if atEOF {
return len(data), data, nil
}
return 0, nil, nil
}
然后绑定:
scanner := bufio.NewScanner(r)
scanner.Split(splitOnTriplePipe)
for scanner.Scan() {
fmt.Println(scanner.Text()) // 每次拿到一段,不含 |||
}
中文/子词分词(如 BPE)必须用专用库,别手搓
大模型场景下的分词(比如对接 GPT 的 cl100k_base 编码表)和传统空格分词完全不同:它要 UTF-8 字节对齐、查合并规则表、处理特殊前缀()、支持 byte fallback。
直接后果:
- 用
strings.Fields()处理中文 → 全切成单字,语义全丢 - 用
bufio.Scanner配空格 Split → 对 “人工智能”、“iPhone15” 这类词完全无感 - 自己实现 BPE → 很容易在字节对查找、递归合并、缓存失效上出错,性能还比不过
tiktoken-go
推荐路径:
- OpenAI 系模型 → 用
pkoukk/tiktoken-go,预载cl100k_base.tiktoken文件 - 中文搜索/ES 场景 → 接入
gojieba或sego,它们内置词典和 HMM - 极简需求(如日志字段提取)→
strings.SplitN(line, "|", 3)更快更稳
超长行和内存安全是分词扫描器最容易崩的点
bufio.Scanner 默认 64KB 缓冲,遇到 base64 blob 或未换行的 JSON,scanner.Text() 会 panic 报 scanner: token too long。这不是 bug,是设计上的安全限制。
绕过方法只有两个:
- 换
bufio.NewReader+ReadString('\n'),自己控制每次读多少字节 - 提前做长度检查:
if len(line) > 2*1024*1024 { continue },避免后续strings.Contains在超大字符串上反复扫描
还有一个隐形坑:多个 goroutine 共享同一个 []byte 底层切片,file.Read(buf) 会覆盖前一次内容。发送前必须 copy(dst, src),别直接塞进 channel。
真正难的不是怎么切,而是切完之后要不要保留原始偏移、是否要支持回溯、是否要兼容多编码——这些细节一旦忽略,线上跑几天后就会发现某类日志永远搜不到。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











