该用 bufio.Reader 而不是 file.Read() 时:读取文本类大文件(如日志、CSV)且按行或分隔符处理;它合并系统调用、减少内核切换,适合 GB 级逐行解析等场景。

bufio 适合需要减少系统调用、提升 I/O 吞吐的场景,不适合低延迟敏感或极小数据量直通场景。
什么时候该用 bufio.Reader 而不是直接 file.Read()
当你读取的是文本类大文件(如日志、CSV、配置)、且每次处理单位是“行”或“分隔符片段”时,bufio.Reader 显著更优。它把多次 read(2) 系统调用合并成一次底层批量读取(默认 4KB),避免每行都触发内核态切换。
- 典型场景:逐行解析 GB 级日志、按
'|'拆分字段的导入文件、流式读取 HTTP 响应体 - 别用它的情况:只读 10 字节 header、或需精确控制每个字节何时进内核(如加密协议握手)
- 注意
ReadString('\n')会把换行符包含在返回值里,要用strings.TrimSuffix(line, "\n")清理 - 若文件含超长行(>64KB),
Reader没限制,但Scanner默认会报bufio.Scanner: token too long错误
为什么 bufio.Scanner 是大多数文本行处理的首选
它比 Reader.ReadString 更安全、API 更简洁,内部已做缓冲+行切分+错误聚合,还自动跳过 CR/LF 差异。
- 必须调用
scanner.Scan()才真正推进读取,scanner.Text()只是取当前行缓存 —— 这意味着不能在循环里反复调用Text()试图重读 - 默认单行上限 64KB,处理长 JSON 行或 base64 块时务必提前设置:
scanner.Buffer(make([]byte, 0, 128*1024), 2*1024*1024) - 它不支持自定义分隔符(如
';'),要分号分割就得换回Reader.ReadString(';') - 性能上和
Reader接近,但封装了更多边界逻辑(比如 EOF 处理、空行判定)
bufio.Writer 忘记 Flush() 就等于没写
所有写入都先落在内存缓冲区,不调用 Flush() 或关闭 writer(Close() 会隐式 flush),数据就卡在内存里,文件/网络连接收不到。
立即学习“go语言免费学习笔记(深入)”;
- 常见错误:写完就
defer file.Close(),但没defer writer.Flush()—— 文件为空或只有部分内容 - 写入后立即需要可见性(如日志实时刷盘),应在关键点手动
writer.Flush();否则等缓冲区满(默认 4KB)才落盘 - 并发写同一
Writer会 panic 或数据错乱,每个 goroutine 必须持有独立实例 - 若写入目标是
os.Stdout或网络连接,Flush()还影响响应延迟,不能省
缓冲区大小不是越大越好:64KB 缓冲器在千连接服务中可能吃掉几百 MB 内存;而 512B 缓冲器又会让小文件读写频繁击穿到系统调用。实际选 4KB~16KB 平衡多数场景,大吞吐流式处理可试 64KB,但得压测验证。










