bufio.scanner 丢最后一行是因为默认按换行符分割,文件末尾无换行符时该行滞留缓冲区;应检查 scan() 返回值再调用 text(),或改用 readline() 等更可控方式。

为什么 bufio.Scanner 读取文件时会丢掉最后一行?
因为默认的扫描器在遇到换行符(\n)时才返回一行,而文件末尾若无换行符,Scan() 就会返回 false,最后一行内容被留在缓冲区里没吐出来。
常见错误现象:diff 工具对比结果少一行,或发现两个看似相同的文件被判定为不一致——其实只是右边文件末尾缺换行。
- 用
scanner.Err()检查是否因 I/O 错误提前终止,而不是忽略它 - 读完所有
Scan()后,调用scanner.Text()前先确认Scan()返回了true;否则可能拿到上一次的残留内容 - 更稳妥的做法:改用
bufio.Reader.ReadLine()或直接读全部再strings.Split(),尤其当你要保证“行数严格对应”时
逐行对比时,怎么处理 Windows 和 Unix 换行符差异?
Windows 用 \r\n,Unix/Linux/macOS 用 \n。如果直接比字符串,哪怕内容一样也会被判不同。
使用场景:跨平台协作的配置文件、日志、生成的代码模板。
立即学习“go语言免费学习笔记(深入)”;
- 简单做法:读入后统一用
strings.TrimSuffix(line, "\r")去掉可能的\r - 别用
strings.TrimSpace()——它会吃掉行首尾空格,破坏缩进敏感的内容(比如 YAML、Python) - 如果要保留原始格式做高亮或 patch 输出,得单独记录换行符类型,不能只存
line字符串
os.Open() 打开大文件时卡住或 OOM 怎么办?
不是 os.Open() 卡,是后续一次性读全(比如 ioutil.ReadAll())把内存撑爆了。逐行读本意就是避开这个问题,但写法不对照样崩。
性能影响:1GB 文件用 ReadAll() 可能占 1.2GB 内存;用 Scanner 通常只占几 KB 缓冲区。
- 永远不要对未知大小的文件调用
io.ReadAll()或bytes.Buffer.ReadFrom() -
bufio.NewScanner()默认缓冲区是 64KB,够用;如需更大行(比如超长 JSON 行),用scanner.Buffer(make([]byte, 1024), 1 手动设上限 - 打开文件后记得
defer f.Close(),否则 Linux 下 fd 耗尽后open: too many open files错误会静默失败
对比逻辑里该不该忽略空白行和注释?
取决于用途。Git 的 git diff 默认不忽略;但做配置校验或模板渲染检查时,往往要跳过。
容易踩的坑:正则匹配注释太暴力,比如 Go 的 // 注释若出现在字符串里(log.Printf("// this is not a comment"))就会误判。
- 真要过滤注释,优先用对应语言的 parser(如
go/parser),而不是字符串扫描 - 空白行判断用
strings.TrimSpace(line) == "",别只看len(line) == 0 - 如果两边都跳过空白/注释,行号映射就断了——输出 “第 5 行不同” 会误导人,此时建议保留原始行号 + 标记跳过状态
真正麻烦的是混合场景:一边是人工编辑的带注释配置,一边是自动生成的无注释 JSON ——这种没法靠通用规则解决,得提前约定输入规范。










