因为 bufio.Scanner 默认单行上限 64KB,遇超长行(如 base64 或 JSON)直接 panic;应改用 bufio.Reader.ReadString('\n') 手动控制,避免缓冲区溢出与内存失控。

超长文本文件读取时 Scanner 为什么会 panic
因为 bufio.Scanner 默认对单行设了硬性长度上限(64KB),遇到日志里混着 base64 或超长 JSON 字段,scanner.Text() 直接抛 scanner: token too long。这不是配置漏了,是设计如此——它用固定缓冲区预读、切分,没打算处理 MB 级单行。
- 别试
scanner.Buffer(make([]byte, 10*1024*1024), 10*1024*1024)把上限拉太高:内存暴涨,且仍可能被非法 UTF-8 截断 - 改用
bufio.NewReader(file).ReadString('\n'),自己控制每次读取行为;遇到超长行先截断或跳过,再做后续处理 - 每行读完立刻检查编码:
if !utf8.ValidString(line) { continue },避免传给分词器后 panic 或结果错乱
中文分词库选 gse 还是 gojieba
业务文本含大量专有名词(如“TiDB”“小红书种草”“大模型推理”),优先选 gojieba;若需轻量嵌入、不依赖外部词典、部署简单,gse 更稳。
-
gojieba初始化必须显式指定词典路径:gojieba.NewJiebaFromPath("./dict"),否则报cannot find dictionary;更推荐gojieba.NewJiebaFromEmbed(),自带精简词典,无文件依赖 -
gse加自定义词典要严格 UTF-8 编码(无 BOM),每行格式为词 词频 词性,比如微信支付 95 nz;加载后必须调seg.Cut()才生效,不是 reload 就自动更新 - 两者都不并发安全:多个 goroutine 共享同一实例会 crash;用
sync.Pool复用比每次new更省,但记得归还:defer pool.Put(jieba)
分片读取时跨块行丢失怎么对齐
用 os.File.Seek() 粗切 10MB 块后直接起 Scanner,会导致横跨块边界的行被劈成两半——前半截在块 A 缓存未触发 Scan,后半截在块 B 当新行处理,整行消失。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 每个分片起点必须对齐到完整行首:Seek 后用
io.ReadBytes('\n')往前找最近换行符,再file.Seek(1, io.SeekCurrent)跳过它 - 分片末尾若不以
'\n'结尾(即最后一行没换行),需额外读下一块开头直到遇到'\n',拼接后再送入分词器,否则该行永远丢失 - 别把分片逻辑和分词耦合太紧:先保证行完整读出,再喂给
seg.Cut([]byte(line)),中间不要累积 slice 或 map
LangChain Go 的文本分割器适合直接用于分词吗
不适合。langchaingo 的 RecursiveCharacter 或 MarkdownTextSplitter 是为 LLM 上下文窗口服务的语义分块工具,不是中文分词器——它不识别“微信支付”是词,只会按标点/空格切,也无法处理歧义或未登录词。
立即学习“go语言免费学习笔记(深入)”;
- 它输出的是
[]string片段,不是词粒度结果;想提取关键词,还得在每个片段上再跑一遍gojieba.Cut() - 若你目标是向量检索+RAG,先用
langchaingo分块,再对每块做分词和 embedding;但若目标是搜索索引或词频统计,绕过它,直接流式分词更准更快 - 真正容易被忽略的是:分块重叠(overlap)值设太大(如 500 字符),会让同一词反复出现在多个块里,后续去重逻辑必须考虑位置偏移,否则统计失真

















