IO 操作

34.1 io 包核心接口

Go 的 io 包定义了几个核心接口,是所有 IO 操作的基础。

io.Reader 接口

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

io.Writer 接口

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

io.Closer 接口

type Closer interface {
    Close() error
}

组合接口

type ReadWriter interface {
    Reader
    Writer
}

type ReadCloser interface {
    Reader
    Closer
}

type WriteCloser interface {
    Writer
    Closer
}

type ReadWriteCloser interface {
    Reader
    Writer
    Closer
}

34.2 实现 Reader 接口

自定义 Reader

package main

import (
    "fmt"
    "io"
)

type StringReader struct {
    data []byte
    pos  int
}

func NewStringReader(s string) *StringReader {
    return &StringReader{
        data: []byte(s),
    }
}

func (r *StringReader) Read(p []byte) (n int, err error) {
    if r.pos >= len(r.data) {
        return 0, io.EOF
    }

    n = copy(p, r.data[r.pos:])
    r.pos += n
    return n, nil
}

func main() {
    reader := NewStringReader("Hello, Go!")

    buf := make([]byte, 5)
    for {
        n, err := reader.Read(buf)
        if err == io.EOF {
            break
        }
        fmt.Printf("读取: %s\n", buf[:n])
    }
}

使用 io.LimitReader

package main

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

func main() {
    reader := strings.NewReader("Hello, Go! This is a long string.")

    // 限制只读取 10 字节
    limitedReader := io.LimitReader(reader, 10)

    data, _ := io.ReadAll(limitedReader)
    fmt.Println(string(data)) // Hello, Go!
}

使用 io.TeeReader

package main

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

func main() {
    reader := strings.NewReader("Hello, Go!")
    var buf bytes.Buffer

    // TeeReader 同时读取并写入到 buf
    teeReader := io.TeeReader(reader, &buf)

    data, _ := io.ReadAll(teeReader)
    fmt.Println("读取的数据:", string(data))
    fmt.Println("缓冲区数据:", buf.String())
}

34.3 实现 Writer 接口

自定义 Writer

package main

import (
    "fmt"
    "io"
)

type CountWriter struct {
    count int
}

func (w *CountWriter) Write(p []byte) (n int, err error) {
    w.count += len(p)
    return len(p), nil
}

func main() {
    writer := &CountWriter{}

    io.WriteString(writer, "Hello")
    io.WriteString(writer, ", Go!")

    fmt.Printf("总共写入: %d 字节\n", writer.count)
}

使用 io.MultiWriter

package main

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

func main() {
    var buf bytes.Buffer

    // 同时写入到文件和缓冲区
    multiWriter := io.MultiWriter(os.Stdout, &buf)

    io.WriteString(multiWriter, "Hello, Go!\n")

    fmt.Println("缓冲区内容:", buf.String())
}

34.4 复制数据

io.Copy

package main

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

func main() {
    reader := strings.NewReader("Hello, Go!")
    var writer strings.Builder

    // 复制数据
    n, err := io.Copy(&writer, reader)
    if err != nil {
        fmt.Println("复制失败:", err)
        return
    }

    fmt.Printf("复制了 %d 字节\n", n)
    fmt.Println("内容:", writer.String())
}

io.CopyBuffer

package main

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

func main() {
    reader := strings.NewReader("Hello, Go!")
    var writer strings.Builder

    // 使用自定义缓冲区
    buf := make([]byte, 4)
    n, err := io.CopyBuffer(&writer, reader, buf)
    if err != nil {
        fmt.Println("复制失败:", err)
        return
    }

    fmt.Printf("复制了 %d 字节\n", n)
    fmt.Println("内容:", writer.String())
}

io.CopyN

package main

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

func main() {
    reader := strings.NewReader("Hello, Go!")
    var writer strings.Builder

    // 只复制 5 字节
    n, err := io.CopyN(&writer, reader, 5)
    if err != nil {
        fmt.Println("复制失败:", err)
        return
    }

    fmt.Printf("复制了 %d 字节\n", n)
    fmt.Println("内容:", writer.String()) // Hello
}

34.5 缓冲 IO

bufio.Reader

package main

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

func main() {
    reader := bufio.NewReader(strings.NewReader("Hello\nGo\nWorld"))

    // 读取一行
    line, _ := reader.ReadString('\n')
    fmt.Print("第一行:", line)

    // 读取一行
    line, _ = reader.ReadString('\n')
    fmt.Print("第二行:", line)

    // 读取剩余内容
    remaining, _ := reader.ReadString('\n')
    fmt.Print("剩余:", remaining)
}

bufio.Writer

package main

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

func main() {
    file, _ := os.Create("output.txt")
    defer file.Close()

    writer := bufio.NewWriter(file)

    writer.WriteString("第一行\n")
    writer.WriteString("第二行\n")
    writer.WriteString("第三行\n")

    // 刷新缓冲区
    writer.Flush()

    fmt.Println("写入完成")
}

bufio.Scanner

package main

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

func main() {
    text := `第一行
第二行
第三行`

    scanner := bufio.NewScanner(strings.NewReader(text))

    lineNum := 1
    for scanner.Scan() {
        fmt.Printf("第 %d 行: %s\n", lineNum, scanner.Text())
        lineNum++
    }

    if err := scanner.Err(); err != nil {
        fmt.Println("扫描错误:", err)
    }
}

自定义扫描分割

package main

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

func main() {
    text := "apple,banana,orange,grape"

    scanner := bufio.NewScanner(strings.NewReader(text))

    // 自定义分割函数(按逗号分割)
    scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
        for i := 0; i < len(data); i++ {
            if data[i] == ',' {
                return i + 1, data[:i], nil
            }
        }
        if atEOF && len(data) > 0 {
            return len(data), data, nil
        }
        return 0, nil, nil
    })

    for scanner.Scan() {
        fmt.Println(scanner.Text())
    }
}

34.6 管道

io.Pipe

package main

import (
    "fmt"
    "io"
)

func main() {
    r, w := io.Pipe()

    // 写入协程
    go func() {
        defer w.Close()
        w.Write([]byte("Hello, "))
        w.Write([]byte("Go!"))
    }()

    // 读取
    data, _ := io.ReadAll(r)
    fmt.Println(string(data))
}

管道实际应用

package main

import (
    "compress/gzip"
    "fmt"
    "io"
    "strings"
)

func main() {
    r, w := io.Pipe()

    // 压缩协程
    go func() {
        defer w.Close()
        gw := gzip.NewWriter(w)
        defer gw.Close()
        io.WriteString(gw, "Hello, Go! This is a test string for compression.")
    }()

    // 读取压缩数据
    data, _ := io.ReadAll(r)
    fmt.Printf("压缩后大小: %d 字节\n", len(data))
}

34.7 字符串和字节操作

strings.Reader

package main

import (
    "fmt"
    "strings"
)

func main() {
    reader := strings.NewReader("Hello, Go!")

    // 读取所有
    data, _ := io.ReadAll(reader)
    fmt.Println(string(data))

    // 重置位置
    reader.Seek(0, 0)

    // 读取部分
    buf := make([]byte, 5)
    reader.Read(buf)
    fmt.Println(string(buf)) // Hello
}

strings.Builder

package main

import (
    "fmt"
    "strings"
)

func main() {
    var builder strings.Builder

    builder.WriteString("Hello")
    builder.WriteString(", ")
    builder.WriteString("Go!")

    fmt.Println(builder.String())
    fmt.Printf("长度: %d\n", builder.Len())

    // 重置
    builder.Reset()
    builder.WriteString("New content")
    fmt.Println(builder.String())
}

bytes.Buffer

package main

import (
    "bytes"
    "fmt"
)

func main() {
    var buf bytes.Buffer

    buf.WriteString("Hello")
    buf.WriteString(", Go!")

    fmt.Println(buf.String())

    // 读取
    data := make([]byte, 5)
    buf.Read(data)
    fmt.Println(string(data)) // Hello

    // 剩余内容
    fmt.Println(buf.String()) // , Go!
}

34.8 实用函数

io.WriteString

package main

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

func main() {
    // 写入到标准输出
    io.WriteString(os.Stdout, "Hello, Go!\n")

    // 写入到文件
    file, _ := os.Create("output.txt")
    defer file.Close()
    io.WriteString(file, "Hello, File!\n")

    // 写入到 strings.Builder
    var builder strings.Builder
    io.WriteString(&builder, "Hello, Builder!")
    fmt.Println(builder.String())
}

io.ReadAll

package main

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

func main() {
    reader := strings.NewReader("Hello, Go!")
    data, _ := io.ReadAll(reader)
    fmt.Println(string(data))
}

io.ReadFull

package main

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

func main() {
    reader := strings.NewReader("Hello, Go!")
    buf := make([]byte, 5)

    // 精确读取 5 字节
    n, err := io.ReadFull(reader, buf)
    fmt.Printf("读取了 %d 字节: %s\n", n, string(buf))
}

io.ReadAtLeast

package main

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

func main() {
    reader := strings.NewReader("Hello, Go!")
    buf := make([]byte, 10)

    // 至少读取 5 字节
    n, err := io.ReadAtLeast(reader, buf, 5)
    fmt.Printf("读取了 %d 字节: %s\n", n, string(buf[:n]))
}

34.9 小结

本章深入讲解了 Go 语言的 IO 操作:

  1. 核心接口:Reader、Writer、Closer 及其组合
  2. 自定义实现:实现 Reader 和 Writer 接口
  3. 复制数据:io.Copy、io.CopyN、io.CopyBuffer
  4. 缓冲 IO:bufio.Reader、bufio.Writer、bufio.Scanner
  5. 管道:io.Pipe 实现协程间通信
  6. 字符串操作:strings.Reader、strings.Builder
  7. 字节操作:bytes.Buffer
  8. 实用函数:io.WriteString、io.ReadAll 等

IO 操作是 Go 语言的核心功能,掌握它能让你更好地处理数据流。在下一章中,我们将学习 JSON 和 XML 处理。