bufio.Scanner是最常用且安全的按行读文件方式,它自动处理换行符、内存友好并内置单行长度限制;需注意扩容、空行判断、复用实例及编码控制等细节。

用 bufio.Scanner 是最常用也最安全的选择
绝大多数场景下,直接用 bufio.Scanner 读文件就对了。它默认按行切分、自动处理换行符、内存友好,还内置了单行长度限制防爆栈。
常见错误是手动拼接 bufio.Reader.ReadBytes('\n') 或反复调用 ReadString('\n')——容易漏掉最后一行(没换行符时)、遇到 \r\n 处理不一致、或缓冲区溢出没察觉。
-
Scanner默认单行上限是 64KB,超长会报scanner: token too long,真要读超长行得先调ScanLines+Bufio.Scanner.Buffer扩容 - 如果文件含大量空行或纯控制字符,
Scanner.Text()返回空字符串,但Scanner.Scan()仍返回true,别误判为 EOF - 不要在循环里重复 new 一个
Scanner;复用同一个实例,传入新os.File时记得先file.Seek(0, 0)
需要精确控制编码或二进制边界时,改用 bufio.Reader
Scanner 内部就是基于 Reader 实现的,但屏蔽了底层细节。一旦你要处理非 UTF-8 编码、跳过 BOM、或读完一行后把多余字节“塞回去”,就得上 Reader。
典型场景:读 CSV 文件头判断编码,或解析混合文本/二进制协议(比如 HTTP 响应体前几行是文本头,后面是 raw body)。
立即学习“go语言免费学习笔记(深入)”;
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
-
Reader.ReadString('\n')遇到 EOF 且没找到换行符时,返回已读内容 +io.EOF,不是错误;要区分“正常结束”和“出错”,得检查 err 是否等于io.EOF -
Reader.UnreadRune()只能回退一个 Unicode 码点,不是字节;如果刚读的是多字节 UTF-8 字符(比如中文),回退后可能破坏字节流 - 用
Reader.Peek(n)预读时,n 不能超过缓冲区大小(默认 4096),否则 panic;提前用Reader.Size调整
大文件或性能敏感场景,避免 Scanner.Text() 的字符串拷贝
每次调 Scanner.Text() 都会把当前行 bytes 转成新字符串,底层是 string(buf[start:end]) —— 看似没分配,实则每次都会生成新 string header,GC 压力不小。百万行文件里这开销很可观。
如果你只做简单匹配(比如 grep)、或后续还要转成 []byte 处理,直接用 Scanner.Bytes() 更省。
-
Scanner.Bytes()返回的是内部缓冲区的切片,下一行扫描会覆盖它;必须立刻拷贝(append([]byte{}, line...))或处理完再进下一轮 - 别在 goroutine 里异步用
Bytes()结果——缓冲区是共享的,数据随时被改写 - 如果只是统计行数、校验格式,甚至可以不用
Text()或Bytes(),直接Scan()然后丢弃,最快
Windows 换行符 \r\n 导致行尾多出 \r 怎么办
Go 标准库本身不自动 strip \r,Scanner 和 Reader.ReadString 都原样保留。你看到的 “xxx\r” 是真实内容,不是显示问题。
这不是 bug,是设计:让使用者明确决定是否清理。很多协议(如 SMTP、HTTP)要求 \r\n 严格存在,删掉反而错。
- 快速清理:用
strings.TrimRight(line, "\r\n"),比strings.ReplaceAll(line, "\r", "")安全(避免误删正文里的 \r) - 如果文件确定是 Windows 文本且每行都带 \r\n,初始化
Scanner前可设Scanner.Split(bufio.ScanLines),再自己处理每行末尾 - 更稳妥的做法是在读取后统一 normalize 行结尾:
strings.Map(func(r rune) rune { if r == '\r' { return -1 }; return r }, line)
真正麻烦的从来不是“怎么读”,而是读完之后那行数据到底算什么——是用户输入?配置项?还是协议载荷?不同语义,对换行、空格、编码的容忍度天差地别。别急着封装通用“按行读取函数”,先想清楚这一行在你业务里,是不是真的该被当“一行”来对待。

















