默认 bufio.scanner 单行限制 64kb,超长需 sc.buffer 调大容量与上限;不确定行长时改用 bufio.reader.readstring;strings.split 不兼容 \r\n,应先 trimright 再切分或用 fieldsfunc;大文件禁用 ioutil.readfile,优先流式处理并显式检查 scanner.err()。

bufio.Scanner 读大文件时 panic: scanner: token too long 怎么办
这是最常踩的坑:默认 bufio.Scanner 最多只允许单行 64KB,超长日志、嵌套 JSON 或 CSV 中含 Base64 字段时直接崩溃。不是 bug,是设计限制。
- 临时解法:调大缓冲上限,但必须同时指定底层数组容量和最大 token 长度,例如
sc.Buffer(make([]byte, 0, 256*1024), 2*1024*1024)(底层数组初始 256KB,硬上限 2MB) - 硬上限一旦突破仍会 panic,所以真正不确定行长时,别硬扛——换成
bufio.Reader.ReadString('\n')更稳妥 -
strings.Split完全不处理这个,但它会把整块内存加载进来,超大文件直接 OOM,不能当“备选方案”用
strings.Split 处理换行符不兼容 \r\n 怎么办
Windows 文件末尾带 \r\n,Linux 是 \n,strings.Split(content, "\n") 会导致最后一行多出 \r,后续 trim 或解析失败。
- 不要手动
strings.ReplaceAll(content, "\r\n", "\n")—— 这会把所有\r都干掉,破坏合法内容(比如某些协议字段含\r) - 正确做法:先用
strings.TrimRight(content, "\r\n")去掉末尾冗余,再strings.Split;或更彻底,用strings.FieldsFunc(content, func(r rune) bool { return r == '\r' || r == '\n' })按任意换行符切分 - 注意:
strings.Split返回的切片里可能含空字符串(比如文件末尾多一个换行),得自己strings.TrimSpace过滤
逐行读取后要存进 []string,用 bufio.Scanner 还是 ioutil.ReadFile
别看代码短就选 ioutil.ReadFile + strings.Split。它适合几 MB 以下的小配置文件,超过 10MB 就该换方案。
-
ioutil.ReadFile把整个文件塞进[]byte,1GB 文件 ≈ 1GB 堆内存,GC 卡死,进程可能被系统 kill -
bufio.Scanner是流式读取,内存占用恒定(只存当前行),但需手动收集到切片:lines = append(lines, scanner.Text()) - 如果真要切片且文件很大,优先写成函数返回
[]string,并在调用侧控制是否缓存全部结果——很多场景其实只需要遍历,根本不用存全量
按行处理但不需要存全量,怎么避免内存泄漏
常见错误是开了 defer file.Close() 就以为万事大吉,其实漏了 scanner 的状态管理。
-
scanner.Scan()返回false时,必须立刻检查scanner.Err(),否则 IO 错误会被静默吞掉,后续逻辑可能基于脏数据运行 - 别在循环里反复创建新切片或字符串拼接,比如
result += line—— 这会不断分配新内存,改用bytes.Buffer累加 - 如果处理中要起 goroutine,注意
line是循环变量,得传scanner.Text()的拷贝,否则所有 goroutine 最后都拿到同一行
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











