强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Go 语言完全指南 / 17 - I/O:io.Reader/Writer、bufio、文件操作

17 - I/O

17.1 io.Reader 和 io.Writer

// io 包的核心接口
type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}
组合接口说明
io.ReadWriter同时读写
io.ReadCloser读取并关闭
io.WriteCloser写入并关闭
io.ReadWriteCloser读写并关闭
io.ReadSeeker读取并定位
io.Seeker定位(Seek)
package main

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

func main() {
    // strings.Reader 实现 io.Reader
    r1 := strings.NewReader("Hello, World!")
    buf := make([]byte, 5)
    n, err := r1.Read(buf)
    fmt.Printf("读取 %d 字节: %s\n", n, buf[:n])

    // bytes.Buffer 同时实现 io.Reader 和 io.Writer
    var buf2 bytes.Buffer
    buf2.WriteString("你好世界")
    fmt.Println(buf2.String())

    // 通用的复制函数
    src := strings.NewReader("Go 语言真棒!")
    var dst bytes.Buffer
    n2, err := io.Copy(&dst, src)
    fmt.Printf("复制 %d 字节: %s\n", n2, dst.String())

    // 读取所有数据
    r2 := strings.NewReader("完整内容")
    data, _ := io.ReadAll(r2)
    fmt.Println(string(data))
}

自定义 Reader/Writer

// 自定义 Reader:将读取的内容转大写
type UpperReader struct {
    r io.Reader
}

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

// 自定义 Writer:带前缀输出
type PrefixWriter struct {
    prefix string
    w      io.Writer
}

func (pw *PrefixWriter) Write(p []byte) (int, error) {
    _, err := pw.w.Write([]byte(pw.prefix))
    if err != nil {
        return 0, err
    }
    return pw.w.Write(p)
}

func main() {
    // 使用自定义 Reader
    r := &UpperReader{r: strings.NewReader("hello world")}
    data, _ := io.ReadAll(r)
    fmt.Println(string(data)) // HELLO WORLD

    // 使用自定义 Writer
    pw := &PrefixWriter{prefix: "[LOG] ", w: os.Stdout}
    fmt.Fprintln(pw, "这是一条日志")
}

17.2 bufio 包

缓冲 I/O,减少系统调用次数。

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

func main() {
    // bufio.Reader
    r := bufio.NewReader(strings.NewReader("Hello\nWorld\nGo\n"))
    
    // 按行读取
    for {
        line, err := r.ReadString('\n')
        if err != nil {
            break
        }
        fmt.Print("行:", line)
    }

    // bufio.Scanner(更方便的按行读取)
    scanner := bufio.NewScanner(strings.NewReader("Hello\nWorld\nGo"))
    for scanner.Scan() {
        fmt.Println("扫描:", scanner.Text())
    }
    if err := scanner.Err(); err != nil {
        fmt.Println("扫描错误:", err)
    }

    // bufio.Writer
    var buf strings.Builder
    w := bufio.NewWriter(&buf)
    for i := 0; i < 5; i++ {
        fmt.Fprintf(w, "Line %d\n", i)
    }
    w.Flush() // 必须调用 Flush 将缓冲数据写入底层 Writer
    fmt.Print(buf.String())
}

从 stdin 读取输入

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    fmt.Println("请输入你的名字:")
    
    if scanner.Scan() {
        name := scanner.Text()
        fmt.Printf("你好, %s!\n", name)
    }
}

Scanner 自定义分割

// 按单词分割
scanner := bufio.NewScanner(strings.NewReader("Hello World Go"))
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
    fmt.Println(scanner.Text())
}

// 自定义缓冲区大小
scanner := bufio.NewScanner(os.Stdin)
scanner.Buffer(make([]byte, 1024), 1024*1024) // 最大 1MB

17.3 文件操作

package main

import (
    "fmt"
    "io"
    "os"
    "path/filepath"
)

func main() {
    // 写入文件
    err := os.WriteFile("test.txt", []byte("Hello, Go!"), 0644)
    if err != nil {
        panic(err)
    }
    defer os.Remove("test.txt")

    // 读取文件
    data, err := os.ReadFile("test.txt")
    if err != nil {
        panic(err)
    }
    fmt.Println(string(data))

    // 低级文件操作
    f, err := os.Create("test2.txt")
    if err != nil {
        panic(err)
    }
    defer f.Close()
    defer os.Remove("test2.txt")

    // 写入
    f.WriteString("Hello, ")
    f.Write([]byte("World!"))

    // 读取
    f2, _ := os.Open("test2.txt")
    content, _ := io.ReadAll(f2)
    fmt.Println(string(content))
    f2.Close()
}

文件信息

func main() {
    info, err := os.Stat("test.txt")
    if err != nil {
        if os.IsNotExist(err) {
            fmt.Println("文件不存在")
        }
        return
    }

    fmt.Println("文件名:", info.Name())
    fmt.Println("大小:", info.Size(), "bytes")
    fmt.Println("权限:", info.Mode())
    fmt.Println("修改时间:", info.ModTime())
    fmt.Println("是否目录:", info.IsDir())
}

目录操作

func main() {
    // 创建目录
    os.MkdirAll("a/b/c", 0755)
    defer os.RemoveAll("a")

    // 读取目录
    entries, _ := os.ReadDir(".")
    for _, entry := range entries {
        fmt.Printf("%s (dir=%v)\n", entry.Name(), entry.IsDir())
    }

    // 遍历目录树
    filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }
        fmt.Println(path, info.Size())
        return nil
    })
}

临时文件

func main() {
    // 创建临时文件
    tmpFile, err := os.CreateTemp("", "example-*.txt")
    if err != nil {
        panic(err)
    }
    defer os.Remove(tmpFile.Name())
    fmt.Println("临时文件:", tmpFile.Name())

    tmpFile.WriteString("临时内容")
    tmpFile.Close()

    // 创建临时目录
    tmpDir, err := os.MkdirTemp("", "example-")
    if err != nil {
        panic(err)
    }
    defer os.RemoveAll(tmpDir)
    fmt.Println("临时目录:", tmpDir)
}

17.4 io 包实用函数

import (
    "io"
    "strings"
)

func main() {
    r := strings.NewReader("Hello, World!")

    // 有限读取
    limited := io.LimitReader(r, 5)
    data, _ := io.ReadAll(limited)
    fmt.Println(string(data)) // Hello

    // 节流 Reader(读取不超过 N 字节)
    r2 := strings.NewReader("Long content here")
    lr := io.LimitReader(r2, 4)

    // 多路复用(TeeReader:读取的同时写入)
    src := strings.NewReader("Hello")
    var buf bytes.Buffer
    tee := io.TeeReader(src, &buf)
    data2, _ := io.ReadAll(tee)
    fmt.Println("读取:", string(data2))
    fmt.Println("复制:", buf.String())

    // MultiReader:合并多个 Reader
    r3 := io.MultiReader(
        strings.NewReader("Hello "),
        strings.NewReader("World"),
    )
    data3, _ := io.ReadAll(r3)
    fmt.Println(string(data3))

    // MultiWriter:写入多个 Writer
    var buf1, buf2 bytes.Buffer
    mw := io.MultiWriter(&buf1, &buf2)
    mw.Write([]byte("Hello"))
    fmt.Println(buf1.String(), buf2.String()) // Hello Hello
}

17.5 pipes

func main() {
    // io.Pipe:同步管道
    pr, pw := io.Pipe()

    go func() {
        fmt.Fprintln(pw, "Hello from writer")
        pw.Close()
    }()

    data, _ := io.ReadAll(pr)
    fmt.Println(string(data))

    // os.Pipe:操作系统管道
    r, w, _ := os.Pipe()
    w.WriteString("OS pipe data")
    w.Close()
    data2, _ := io.ReadAll(r)
    fmt.Println(string(data2))
}

🏢 业务场景

  1. 日志处理:bufio.Writer 缓冲写入日志文件
  2. 配置解析:os.ReadFile 读取 YAML/JSON 配置
  3. 文件上传:io.Copy 将请求体写入文件
  4. 数据管道:io.Pipe 连接生产者和消费者
  5. 批量处理:bufio.Scanner 逐行处理大文件

📖 扩展阅读