← time 包 | bufio 包 →

io 包 - Go 输入输出详解

io 包提供了 I/O 操作的基本接口和工具函数,是 Go 标准库的基石。理解 io.Reader、io.Writer 等核心接口是掌握 Go I/O 编程的关键。

📌 核心概念

📖

Reader

读取数据

Read(p []byte)
✍️

Writer

写入数据

Write(p []byte)
🔗

组合接口

ReaderFrom/WriterTo

io.Copy
🛠️

工具函数

Copy/Limit/TEE

io 包工具

io.Reader 接口

📖 Reader 定义和使用

package main

import (
    "fmt"
    "io"
    "os"
    "strings"
)

// io.Reader 接口定义
// type Reader interface {
//     Read(p []byte) (n int, err error)
// }

func main() {
    // 从字符串读取
    reader := strings.NewReader("Hello, World!")
    buf := make([]byte, 5)
    
    // 读取数据
    n, err := reader.Read(buf)
    fmt.Printf("Read %d bytes: %s\n", n, buf[:n])
    // Read 返回 io.EOF 表示读取完成
    
    // 读取整个内容
    reader2 := strings.NewReader("Hello")
    data, _ := io.ReadAll(reader2)
    fmt.Printf("All: %s\n", data)
    
    // 读取固定长度
    reader3 := strings.NewReader("0123456789")
    data2 := make([]byte, 3)
    io.ReadFull(reader3, data2) // 必须读满
    fmt.Printf("Full: %s\n", data2)
    
    // 读取至少 N 字节
    reader4 := strings.NewReader("abc")
    buf4 := make([]byte, 10)
    n, _ = io.ReadAtLeast(reader4, buf4, 2)
    fmt.Printf("AtLeast: %s\n", buf4[:n])
}

💡 Reader 要点

  • 返回值: n 为读取字节数,err 为错误
  • io.EOF: 读取完成时返回,不是错误
  • 阻塞: 没有数据时 Read 会阻塞
  • 实现者: *File, *bytes.Buffer, *strings.Reader 等

io.Writer 接口

📖 Writer 定义和使用

package main

import (
    "bytes"
    "fmt"
    "io"
    "os"
)

// io.Writer 接口定义
// type Writer interface {
//     Write(p []byte) (n int, err error)
// }

func main() {
    // 写入 bytes.Buffer
    var buf bytes.Buffer
    buf.Write([]byte("Hello"))
    buf.WriteString(", World!")
    fmt.Println(buf.String())
    
    // 写入文件
    file, _ := os.Create("test.txt")
    defer file.Close()
    file.Write([]byte("File content"))
    
    // 多 Writer 合并
    var buf1, buf2 bytes.Buffer
    multi := io.MultiWriter(&buf1, &buf2)
    multi.Write([]byte("Same data"))
    fmt.Printf("buf1: %s, buf2: %s\n", buf1.String(), buf2.String())
    
    // 丢弃写入
    io.Copy(io.Discard, strings.NewReader("Discarded"))
}

io.Copy 数据拷贝

📝 Copy 和相关函数

package main

import (
    "bytes"
    "fmt"
    "io"
    "os"
    "strings"
)

func main() {
    // 基础 Copy
    src := strings.NewReader("Hello, World!")
    var dst bytes.Buffer
    io.Copy(&dst, src)
    fmt.Println(dst.String())
    
    // CopyN: 拷贝 N 字节
    src2 := strings.NewReader("0123456789")
    var dst2 bytes.Buffer
    io.CopyN(&dst2, src2, 5)
    fmt.Println(dst2.String()) // 01234
    
    // 文件拷贝
    srcFile, _ := os.Open("src.txt")
    defer srcFile.Close()
    dstFile, _ := os.Create("dst.txt")
    defer dstFile.Close()
    io.Copy(dstFile, srcFile)
    
    // 带进度拷贝
    copyWithProgress()
}

func copyWithProgress() {
    var src bytes.Buffer
    // 填充大数据...
    
    var dst bytes.Buffer
    written, _ := io.Copy(&dst, &src)
    fmt.Printf("Copied %d bytes\n", written)
}

io 工具函数

📝 常用工具函数

package main

import (
    "bytes"
    "fmt"
    "io"
    "strings"
)

func main() {
    // LimitReader: 限制读取 N 字节
    src := strings.NewReader("0123456789")
    limited := io.LimitReader(src, 5)
    data, _ := io.ReadAll(limited)
    fmt.Println(string(data)) // 01234
    
    // TeeReader: 读取时复制到另一个 Writer
    src2 := strings.NewReader("Hello")
    var teeBuf bytes.Buffer
    tee := io.TeeReader(src2, &teeBuf)
    data2, _ := io.ReadAll(tee)
    fmt.Printf("Read: %s, Tee: %s\n", data2, teeBuf.String())
    
    // SectionReader: 读取文件的某一段
    // section := io.NewSectionReader(file, offset, size)
    
    // Pipe: 创建管道
    pr, pw := io.Pipe()
    go func() {
        pw.Write([]byte("Pipe data"))
        pw.Close()
    }()
    data3, _ := io.ReadAll(pr)
    fmt.Println(string(data3))
}

实现 Reader/Writer

📝 自定义 Reader/Writer

package main

import (
    "errors"
    "fmt"
    "io"
)

// 自定义 Reader: 只返回大写字母
type UpperReader struct {
    src io.Reader
}

func (r *UpperReader) Read(p []byte) (int, error) {
    n, err := r.src.Read(p)
    for i := 0; i < n; i++ {
        if p[i] >= 'a' && p[i] <= 'z' {
            p[i] -= 32
        }
    }
    return n, err
}

// 自定义 Writer: 统计写入字节数
type CountWriter struct {
    Count int64
}

func (c *CountWriter) Write(p []byte) (int, error) {
    c.Count += int64(len(p))
    return len(p), nil
}

func main() {
    // 使用 UpperReader
    src := strings.NewReader("hello world")
    upper := &UpperReader{src}
    data, _ := io.ReadAll(upper)
    fmt.Println(string(data)) // HELLO WORLD
    
    // 使用 CountWriter
    cw := &CountWriter{}
    io.Copy(cw, strings.NewReader("12345"))
    fmt.Printf("Written: %d bytes\n", cw.Count)
}

最佳实践

✅ io 包使用建议

  • 面向接口: 函数参数使用 io.Reader/Writer
  • 缓冲 IO: 大文件使用 bufio 提升性能
  • 错误处理: 检查 io.EOF 和其他错误
  • 资源清理: 使用 defer 关闭文件
  • io.Copy: 优先使用 io.Copy 而非手动循环