bufio.newreader 比 os.file.read 快是因为其用 4kb 缓冲区批量读取,减少系统调用;小文件用 io.readall 更简,大文件行处理必须用 bufio;scanner 默认 64kb 缓冲易溢出;newwriter 需显式 flush 防丢数据;reader 与 scanner 不可混用同一文件。

bufio.NewReader 为什么比 os.File.Read 快?
因为 os.File.Read 每次调用都触发一次系统调用,而 bufio.NewReader 在内存里维护一块缓冲区(默认 4KB),批量从内核读取、按需吐出,大幅减少系统调用次数。尤其在读小块数据(比如逐行、逐 token)时,性能差距可达 5–10 倍。
- 小文件或一次性读完:用
io.ReadAll或os.ReadFile更简单,bufio反而多一层封装 - 大文件 + 行处理(
ReadString('\n')/ReadLine()):必须用bufio.NewReader,否则每行一次 syscall - 注意:缓冲区大小不是越大越好——超过 L1/L2 缓存可能降低局部性;默认
4096已平衡多数场景
reader := bufio.NewReader(file)
for {
line, err := reader.ReadString('\n')
if err == io.EOF {
break
}
// 处理 line(含 '\n')
}bufio.Scanner 的默认缓冲区会爆吗?
会。Scanner 默认缓冲区只有 64 * 1024 字节(64KB),遇到超长行(比如日志中带大段 base64 或 JSON)直接报 scanner: token too long。
- 不要无脑用
Scan()读任意输入流,尤其来源不可控时 - 改法一:调
scanner.Buffer([]byte{}, max)扩容,但内存开销随max线性增长 - 改法二:换回
bufio.Reader+ReadBytes或ReadSlice,自己处理边界和错误 - 注意:
Scanner是为“合理长度的行”设计的,不是通用流处理器
bufio.NewWriter 写文件时 flush 不及时导致数据丢失?
是的。写入 bufio.NewWriter 后不显式 Flush(),程序退出或 panic 时缓冲区内容可能根本没落盘。
- 所有使用
bufio.NewWriter的地方,必须配对defer writer.Flush()(如果后续还有写操作)或在关键点手动Flush() -
Close()会自动Flush(),但仅当底层io.Writer实现了io.Closer(os.File有,bytes.Buffer没有) - 日志类场景慎用大缓冲(如
bufio.NewWriterSize(w, 1),延迟高且 crash 时丢数据风险更大
w := bufio.NewWriter(file) defer w.Flush() // 关键:不能漏 fmt.Fprintln(w, "hello") // 不加 Flush,这里 exit 后文件可能为空
bufio.Reader 和 bufio.Scanner 能混着用同一个 file 吗?
不能。两者都维护独立读位置和缓冲区,混用会导致跳过数据或重复读。
立即学习“go语言免费学习笔记(深入)”;
-
Reader读了几字节后,Scanner从文件开头重读,中间那段就丢了 - 底层
os.File的Read操作是“移动文件偏移量”的,而bufio层的缓冲只是内存拷贝,不改变底层 offset —— 但多个bufio实例之间互不知情 - 正确做法:只选一个抽象层贯穿始终;若需灵活解析,统一用
bufio.Reader,配合Peek、Discard、ReadSlice自行控制
实际开发中,最常被忽略的是:缓冲区大小与业务数据特征是否匹配。一行 2MB 的日志、每秒写 10K 小消息、或嵌套很深的 JSON 流——这些都不是默认 bufio 参数能扛住的,得动手调,而不是迷信“加了 bufio 就快”。











