跳到主要内容

io 接口体系

问题

Go 的 io.Readerio.Writer 接口为什么这么重要?有哪些常用实现?

答案

核心接口

io.Readerio.Writer 是 Go 标准库中最重要的两个接口:

type Reader interface {
Read(p []byte) (n int, err error)
}

type Writer interface {
Write(p []byte) (n int, err error)
}

几乎所有 IO 操作都围绕这两个接口构建——文件、网络连接、HTTP Body、压缩、加密等都实现了它们。

常见实现

类型ReaderWriter说明
*os.File文件
*bytes.Buffer内存缓冲
*bytes.Reader只读字节流
*strings.Reader只读字符串
*strings.Builder高性能拼接
net.Conn网络连接
http.Request.BodyHTTP 请求体
http.ResponseWriterHTTP 响应
*bufio.Reader带缓冲读
*bufio.Writer带缓冲写
*gzip.Readergzip 解压
*gzip.Writergzip 压缩

常用工具函数

// 复制:从 Reader 复制到 Writer
n, err := io.Copy(dst, src)

// 全部读取
data, err := io.ReadAll(reader)

// 限制读取大小
limited := io.LimitReader(reader, 1024*1024) // 最多读 1MB

// 多个 Reader 串联
multi := io.MultiReader(reader1, reader2, reader3)

// 同时写入多个 Writer
multi := io.MultiWriter(writer1, writer2)

// 分流:读取的同时写入另一个 Writer
tee := io.TeeReader(reader, writer)

// Pipe:连接 Reader 和 Writer
pr, pw := io.Pipe()
go func() {
pw.Write(data)
pw.Close()
}()
io.ReadAll(pr)

组合接口

type ReadWriter interface {
Reader
Writer
}

type ReadCloser interface {
Reader
Closer
}

type ReadWriteCloser interface {
Reader
Writer
Closer
}

type ReadSeeker interface {
Reader
Seeker
}

实际应用模式

链式处理(装饰器模式)

// 文件 → gzip 解压 → 读取
file, _ := os.Open("data.gz")
defer file.Close()

gzReader, _ := gzip.NewReader(file) // 装饰: 添加解压能力
defer gzReader.Close()

bufReader := bufio.NewReader(gzReader) // 装饰: 添加缓冲能力

data, _ := io.ReadAll(bufReader)

常见面试问题

Q1: io.Reader 的 Read 方法返回值怎么理解?

答案

Read(p []byte) (n int, err error)
  • n:实际读取的字节数(0 <= n <= len(p)
  • err:错误信息。io.EOF 表示读完了,不是错误
  • n 和 err 可以同时有值:最后一次 Read 可能返回 n > 0, err = io.EOF

正确的读取循环:

buf := make([]byte, 1024)
for {
n, err := reader.Read(buf)
if n > 0 {
process(buf[:n]) // 先处理数据
}
if err != nil {
if err == io.EOF {
break // 正常结束
}
return err // 真正的错误
}
}

Q2: io.Copy 和 io.ReadAll 有什么区别?

答案

  • io.Copy(dst, src)流式复制,不需要把全部数据加载到内存,适合大文件
  • io.ReadAll(reader):一次性读取全部内容到 []byte,适合小数据

大文件必须io.Copy,否则可能 OOM。

Q3: 为什么 Go 的 io 接口这么成功?

答案

  1. 极简:只有一个方法(Read 或 Write),任何类型都容易实现
  2. 组合性:通过装饰器模式自由组合(缓冲 + 压缩 + 加密)
  3. 通用性:文件、网络、内存、压缩、加密全部统一接口
  4. 零耦合:函数只依赖接口,不依赖具体类型

相关链接