Scanner读文件只读第一行是因为未在循环中反复调用scanner.Scan();正确做法是for scanner.Scan()循环读取,用scanner.Text()获取每行内容,并检查scanner.Err()。

Scanner 读文件时为什么只读到第一行就停了
常见错误是把 scanner.Scan() 写成只调用一次,误以为它会自动读完全部——其实它每次只推进一行,必须放在循环里反复调用。
正确做法是用 for scanner.Scan() 持续拉取,直到返回 false(表示 EOF 或出错)。
-
scanner.Scan()返回bool:成功读到一行返回true,失败(含 EOF)返回false - 别在循环外单独调一次
scanner.Scan(),否则只处理首行 - 循环内用
scanner.Text()取当前行内容(不含换行符),scanner.Bytes()取原始字节
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text() // 或 scanner.Bytes()
fmt.Println(line)
}
if err := scanner.Err(); err != nil {
log.Fatal(err) // 注意检查 scanner.Err(),不是 file.Close() 的 err
}
读大文件时内存暴涨甚至 OOM
Scanner 默认缓冲区只有 64KB,遇到超长行会自动扩容;如果文件里有几 MB 甚至上百 MB 的“单行”(比如无换行的 JSON、日志拼接错误),缓冲区会一路涨到吃光内存。
- 用
scanner.Buffer(make([]byte, 64*1024), 1 显式限制最大容量(例如 1MB) - 更稳妥的做法:确认业务是否真需要支持超长行;如不需要,提前设死上限,让
scanner.Scan()遇超长行直接报bufio.Scanner.TooLong - 对真正海量日志,考虑用
bufio.NewReader+ReadString('\n')手动控制,避免 Scanner 内部状态开销
中文乱码或读到空行但实际有内容
根本原因不是 Go 编码问题,而是文件本身带 BOM 或混用 CRLF/LF,而 Scanner 不做任何编码转换和换行归一化。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
-
Scanner按原始字节切分,遇到\r\n、\n、\r都算行结束,但Text()会去掉末尾的\r和\n—— 如果文件是 Windows 的\r\n,你不会察觉;但若某行末尾残留\r(比如不规范编辑器保存),Text()去掉后可能让中文显示异常 - BOM(如 UTF-8 的
\xEF\xBB\xBF)会被当正文读入首行开头,导致第一行字符串以不可见字符起始 - 解决方法:读前用
bytes.TrimPrefix(data, []byte("\xEF\xBB\xBF"))剥 BOM;或用golang.org/x/text/encoding包做显式解码(需先识别编码)
为什么 scanner.Err() 要在循环结束后检查
很多人把 scanner.Err() 放在循环里,每轮都查——这没意义。因为只要 Scan() 还返回 true,说明上一轮没出错;只有当它返回 false 时,才需要靠 Err() 判断是 EOF 还是真实错误。
- EOF 是合法终止,
scanner.Err()此时返回nil - 如果循环中途因 I/O 错误中断,
Scan()返回false,且Err()返回具体错误(如read: connection reset by peer) - 忽略
scanner.Err()可能掩盖磁盘满、权限不足等关键问题,比file.Close()的 err 更早暴露故障点
真正容易被忽略的是:scanner.Err() 必须在 for 循环结束后立刻检查,不能往后拖,也不能只依赖 file.Close() 的返回值。

















