背景
如果 bufio.Reader 解决的是“读取时的缓冲与便利性”,那 bufio.Writer 对应解决的就是“写入时的缓冲与批量输出”。
它最常见的价值有两点:
- 把很多小写入合并,减少底层调用开销
- 提供一个明确的缓冲写出层,让调用方自己决定何时真正刷出数据
核心概念
最常见的创建方式是:
1writer := bufio.NewWriter(w)
这里的 w 可以是任何实现了 io.Writer 的对象。
它为什么更高效
如果你的代码频繁做小块写入,直接写到底层目标可能会产生很多次底层调用。bufio.Writer 会先把这些写入积累到缓冲区,等条件合适时再一次性刷出去。
这在文本输出、协议拼装、日志聚合这类场景里很常见。
最重要的一件事:Flush
bufio.Writer 最经典的坑就是:
- 你调用了
Write - 程序也没报错
- 但目标里看不到完整输出
原因通常就是:你忘了 Flush()。
1if err := writer.Flush(); err != nil {
2 return err
3}
如果你不 Flush,缓冲区里的数据可能还停在内存里,没有真正写到底层目标。
示例代码
1package main
2
3import (
4 "bufio"
5 "bytes"
6 "fmt"
7)
8
9func main() {
10 var buf bytes.Buffer
11 writer := bufio.NewWriter(&buf)
12
13 _, _ = writer.WriteString("hello")
14 _, _ = writer.WriteString(", writer")
15
16 fmt.Println("before flush:", buf.String())
17 _ = writer.Flush()
18 fmt.Println("after flush:", buf.String())
19}
这段代码最能说明 bufio.Writer 的真实语义:WriteString 成功,不代表底层目标已经立刻拿到了最终数据。
使用边界
不要和底层 Writer 混着写
如果你已经创建了 bufio.Writer,就尽量统一通过它写,不要一会儿写缓冲层,一会儿又直接写底层 w。否则输出顺序和缓冲状态很容易变得难以推理。
Flush 通常应该显式处理错误
Flush 不是形式动作,它是真正把缓冲内容写到底层的关键步骤,所以它失败时不能忽略错误。
不是所有写入都值得加 bufio.Writer
如果本来就是一次性大块输出,再额外套一层缓冲未必有明显收益。它更适合高频小写入场景。
我的总结
bufio.Writer是带缓冲的io.Writer包装器- 它最重要的价值是减少小写入的底层开销
- 使用它时最容易踩的坑就是忘记
Flush - 创建了
bufio.Writer后,尽量不要和底层 Writer 混着写 - 它非常适合文本输出、协议拼装和批量写出场景