Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Go 语言标准库 —— io 包(基础接口 & 拷贝函数)


🔹 核心接口

  • 读取接口(最核心)

    基础读取接口(所有读取操作的基石)

    io.Reader interface
    • 定义:

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

    • 说明:

      • io 包最核心的接口
      • 读取 len(p) 字节到 p 中
      • 返回读取的字节数 n 和错误 err
      • 读到末尾时返回 (0, io.EOF)
    • 实现该接口的常见类型:

      • *os.File - 文件读取
      • *bytes.Buffer - 内存缓冲区
      • *strings.Reader - 字符串读取
      • *bytes.Reader - 字节切片读取
      • net.Conn - 网络连接
    • 示例(完整)

        package main
      
        import (
      	  "fmt"
      	  "io"
      	  "os"
       )
      
        func main() {
      	  // os.File 实现了 io.Reader
      	  var r io.Reader = os.Stdin
      
      	  // 或者使用 strings.Reader
      	  // r := strings.NewReader("hello")
      
      	  buf := make([]byte, 10)
      	  n, err := r.Read(buf)
      
      	  fmt.Printf("读取了 %d 字节\n", n)
      	  fmt.Printf("错误:%v\n", err)
      	  fmt.Printf("内容:%s\n", string(buf[:n]))
        }
      
    • 实现 io.Reader 接口的类型详解

      • os.File(文件读取)
        • 说明:文件描述符,实现了 io.Reader、io.Writer、io.Seeker 等接口
        • 打开文件:os.Open(name string) (*os.File, error)
        • 示例:
          package main
          
          import (
          	"fmt"
          	"os"
          )
          
          func main() {
          	// 打开文件
          	file, err := os.Open("test.txt")
          	if err != nil {
          		fmt.Println("打开失败:", err)
          		return
          	}
          	defer file.Close()
          
          	// 读取文件内容
          	buf := make([]byte, 100)
          	n, err := file.Read(buf)
          
          	fmt.Printf("读取了 %d 字节\n", n)
          	fmt.Printf("内容:%s\n", string(buf[:n]))
          }
          
      • *bytes.Buffer(内存缓冲区)
        • 说明:内存中的字节缓冲区,可读写
        • 创建:var buf bytes.Bufferbytes.NewBufferString(s string)
        • 示例:
          package main
          
          import (
          	"bytes"
          	"fmt"
          )
          
          func main() {
          	// 从字符串创建
          	buf := bytes.NewBufferString("Hello World")
          
          	// 读取数据
          	data := make([]byte, 5)
          	n, _ := buf.Read(data)
          
          	fmt.Printf("读取:%s\n", string(data)) // Hello
          	fmt.Printf("剩余:%s\n", buf.String()) //  World
          }
          
      • *strings.Reader(字符串读取器)
        • 说明:将字符串包装为 io.Reader
        • 创建:strings.NewReader(s string) *strings.Reader
        • 示例:
          package main
          
          import (
          	"fmt"
          	"strings"
          )
          
          func main() {
          	// 创建字符串读取器
          	r := strings.NewReader("Go 语言")
          
          	// 读取数据
          	buf := make([]byte, 6) // "Go 语" 的 UTF-8 编码
          	n, _ := r.Read(buf)
          
          	fmt.Printf("读取:%s\n", string(buf[:n]))
          	fmt.Printf("剩余:%s\n", r.String())
          }
          
      • *bytes.Reader(字节切片读取器)
        • 说明:将 []byte 包装为 io.Reader
        • 创建:bytes.NewReader(b []byte) *bytes.Reader
        • 示例:
          package main
          
          import (
          	"bytes"
          	"fmt"
          )
          
          func main() {
          	data := []byte{72, 101, 108, 108, 111} // "Hello"
          	r := bytes.NewReader(data)
          
          	buf := make([]byte, 3)
          	n, _ := r.Read(buf)
          
          	fmt.Printf("读取:%s\n", string(buf)) // Hel
          }
          
  • 写入接口(最核心)

    基础写入接口(所有写入操作的基石)

    io.Writer interface
    • 定义:

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

    • 说明:

      • io 包最核心的写入接口
      • 写入 p 中的数据
      • 返回写入的字节数 n 和错误 err
      • 如果 n != len(p),通常会返回错误
    • 实现该接口的常见类型:

      • *os.File - 文件写入
      • *bytes.Buffer - 内存缓冲区
      • os.Stdout - 标准输出
      • net.Conn - 网络连接
      • *bufio.Writer - 带缓冲的写入器
    • 示例(完整)

      package main
      
      import (
      	"fmt"
      	"io"
      	"os"
      )
      
      func main() {
      	// os.Stdout 实现了 io.Writer
      	var w io.Writer = os.Stdout
      
      	n, err := w.Write([]byte("Hello, Writer!\n"))
      
      	fmt.Printf("写入了 %d 字节\n", n)
      	fmt.Printf("错误:%v\n", err)
      }
      
    • 实现 io.Writer 接口的类型详解

      • os.File(文件写入)
        • 说明:文件描述符,支持写入数据
        • 创建文件:os.Create(name string) (*os.File, error)
        • 示例:
          package main
          
          import (
          	"fmt"
          	"os"
          )
          
          func main() {
          	// 创建文件
          	file, err := os.Create("output.txt")
          	if err != nil {
          		fmt.Println("创建失败:", err)
          		return
          	}
          	defer file.Close()
          
          	// 写入数据
          	n, err := file.Write([]byte("Hello, File!"))
          	if err != nil {
          		fmt.Println("写入失败:", err)
          		return
          	}
          
          	fmt.Printf("写入了 %d 字节\n", n)
          }
          
      • *bytes.Buffer(内存缓冲区)
        • 说明:内存缓冲区,写入的数据会追加到缓冲区
        • 创建:var buf bytes.Buffer
        • 获取内容:buf.String()buf.Bytes()
        • 示例:
          package main
          
          import (
          	"bytes"
          	"fmt"
          )
          
          func main() {
          	var buf bytes.Buffer
          
          	// 写入数据
          	buf.Write([]byte("Hello"))
          	buf.Write([]byte(" "))
          	buf.Write([]byte("World"))
          
          	// 获取内容
          	fmt.Println(buf.String()) // Hello World
          }
          
      • os.Stdout(标准输出)
        • 说明:标准输出(通常是终端)
        • 类型:*os.File
        • 示例:
          package main
          
          import (
          	"fmt"
          	"os"
          )
          
          func main() {
          	// 直接写入标准输出
          	os.Stdout.Write([]byte("Hello, Stdout!\n"))
          
          	// 使用变量接收
          	var w = os.Stdout
          	w.Write([]byte("Hello again!\n"))
          }
          
      • os.Stderr(标准错误输出)
        • 说明:标准错误输出(通常是终端)
        • 类型:*os.File
        • 示例:
          package main
          
          import (
          	"os"
          )
          
          func main() {
          	// 写入错误信息
          	os.Stderr.Write([]byte("Error occurred!\n"))
          }
          
      • *bufio.Writer(带缓冲的写入器)
        • 说明:提供缓冲功能,减少系统调用次数
        • 创建:bufio.NewWriter(w io.Writer) *bufio.Writer
        • 刷新:必须调用 Flush() 将缓冲区内容写入底层 Writer
        • 示例:
          package main
          
          import (
          	"bufio"
          	"os"
          )
          
          func main() {
          	// 创建带缓冲的写入器
          	w := bufio.NewWriter(os.Stdout)
          
          	// 写入数据(先存入缓冲区)
          	w.Write([]byte("Hello"))
          	w.Write([]byte(" "))
          	w.Write([]byte("Buffered"))
          
          	// 必须刷新才能看到输出
          	w.Flush()
          }
          

  • 字节读取接口

    支持逐字节读取

    io.ByteReader interface
    • 定义:

      type ByteReader interface { ReadByte() (byte, error) }

    • 示例(完整)

      package main
      
      import (
      	"fmt"
      	"io"
      	"strings"
      )
      
      func main() {
      	r := strings.NewReader("hello")
      
      	// 类型断言为 ByteReader
      	if br, ok := r.(io.ByteReader); ok {
      		b, err := br.ReadByte()
      		if err != nil {
      			fmt.Println("读取失败:", err)
      			return
      		}
      		fmt.Printf("%c\n", b) // h
      	}
      }
      

  • 字节扫描接口

    在 ByteReader 基础上支持回退

    io.ByteScanner interface
    • 定义:

      type ByteScanner interface { ReadByte() (byte, error) UnreadByte() error }

    • 示例

      r := strings.NewReader("ab")
      
      b, _ := r.ReadByte()
      fmt.Printf("%c\n", b) // a
      
      r.UnreadByte()
      
      b, _ = r.ReadByte()
      fmt.Printf("%c\n", b) // a
      

  • 字节写入接口

    支持逐字节写入

    io.ByteWriter interface
    • 定义:

      type ByteWriter interface { WriteByte(c byte) error }

    • 说明:

      • 只有一个方法:WriteByte(c byte) error
      • bytes.Buffer、bufio.Writer 等都实现了这个接口
    • 实现示例

      package main
      
      import (
      	"bytes"
      	"fmt"
      )
      
      func main() {
      	var buf bytes.Buffer
      
      	// 逐字节写入
      	buf.WriteByte('H')
      	buf.WriteByte('e')
      	buf.WriteByte('l')
      	buf.WriteByte('l')
      	buf.WriteByte('o')
      
      	fmt.Println(buf.String()) // Hello
      }
      
    • bytes.Buffer 常用方法详解

      • 写入单个字节
        • 说明:写入一个字节到缓冲区
        • 方法:WriteByte(c byte) error
        • 示例:
          var buf bytes.Buffer
          buf.WriteByte('A')
          buf.WriteByte('B')
          fmt.Println(buf.String()) // AB
          
      • 写入字符串
        • 说明:写入字符串,返回写入的字节数和错误
        • 方法:WriteString(s string) (int, error)
        • 示例:
          var buf bytes.Buffer
          n, err := buf.WriteString("Hello")
          fmt.Println(n)   // 5
          fmt.Println(err) // <nil>
          fmt.Println(buf.String()) // Hello
          
      • 写入字节切片
        • 说明:写入字节切片,返回写入的字节数和错误
        • 方法:Write(p []byte) (int, error)
        • 示例:
          var buf bytes.Buffer
          n, err := buf.Write([]byte{72, 105}) // Hi 的 ASCII
          fmt.Println(n)   // 2
          fmt.Println(buf.String()) // Hi
          
      • 写入 Unicode 字符
        • 说明:写入一个 rune(Unicode 字符),返回写入的字节数和错误
        • 方法:WriteRune(r rune) (int, error)
        • 示例:
          var buf bytes.Buffer
          n, err := buf.WriteRune('你') // 中文字符
          fmt.Println(n)   // 3(UTF-8 编码占 3 字节)
          fmt.Println(buf.String()) // 你
          
      • 读取数据
        • 说明:从缓冲区读取数据到字节切片,返回读取的字节数和错误
        • 方法:Read(p []byte) (int, error)
        • 注意:读取后缓冲区内容会减少
        • 示例:
          buf := bytes.NewBufferString("Hello")
          data := make([]byte, 3)
          n, _ := buf.Read(data)
          fmt.Println(n)          // 3
          fmt.Println(string(data)) // Hel
          fmt.Println(buf.String()) // lo(剩余内容)
          
      • 读取单个字节
        • 说明:读取并返回下一个字节
        • 方法:ReadByte() (byte, error)
        • 注意:返回 byte 类型,不是 rune
        • 示例:
          buf := bytes.NewBufferString("ABC")
          b, _ := buf.ReadByte()
          fmt.Printf("%c\n", b) // A
          fmt.Println(buf.String()) // BC
          
      • 读取到分隔符
        • 说明:读取直到遇到分隔符字节,返回读取的内容
        • 方法:ReadBytes(delim byte) ([]byte, error)
        • 注意:返回的字节切片包含分隔符
        • 示例:
          buf := bytes.NewBufferString("line1\nline2\n")
          line, _ := buf.ReadBytes('\n')
          fmt.Println(string(line)) // line1\n
          
      • 获取缓冲区内容(字节)
        • 说明:返回缓冲区未读内容的字节切片
        • 方法:Bytes() []byte
        • 注意:返回的切片会随后续写入而变化
        • 示例:
          buf := bytes.NewBufferString("Hello")
          data := buf.Bytes()
          fmt.Println(string(data)) // Hello
          
      • 获取缓冲区内容(字符串)
        • 说明:返回缓冲区未读内容的字符串
        • 方法:String() string
        • 示例:
          buf := bytes.NewBufferString("World")
          s := buf.String()
          fmt.Println(s) // World
          
      • 获取未读长度
        • 说明:返回缓冲区中未读数据的字节数
        • 方法:Len() int
        • 示例:
          buf := bytes.NewBufferString("Hello")
          fmt.Println(buf.Len()) // 5
          buf.ReadByte()
          fmt.Println(buf.Len()) // 4
          
      • 获取容量
        • 说明:返回缓冲区已分配的总容量(包括已读和未读)
        • 方法:Cap() int
        • 示例:
          buf := bytes.NewBufferString("Hello")
          fmt.Println(buf.Cap()) // 初始容量
          
      • 清空缓冲区
        • 说明:重置缓冲区,丢弃所有未读数据
        • 方法:Reset()
        • 注意:清空后 Len() 为 0,但容量不变
        • 示例:
          buf := bytes.NewBufferString("Hello")
          fmt.Println(buf.Len()) // 5
          buf.Reset()
          fmt.Println(buf.Len()) // 0
          
      • 预留空间
        • 说明:预留 n 字节空间,避免多次内存重新分配
        • 方法:Grow(n int)
        • 注意:如果 n 小于当前容量,不会缩小
        • 示例:
          var buf bytes.Buffer
          buf.Grow(100) // 预留 100 字节
          fmt.Println(buf.Cap()) // >= 100
          
      • 截断缓冲区
        • 说明:截断缓冲区到 n 字节
        • 方法:Truncate(n int)
        • 注意:如果 n > Len(),不会扩展
        • 示例:
          buf := bytes.NewBufferString("Hello")
          buf.Truncate(3)
          fmt.Println(buf.String()) // Hel
          
      • 写入到其他 Writer
        • 说明:将缓冲区所有内容写入到另一个 Writer
        • 方法:WriteTo(w io.Writer) (int64, error)
        • 返回:写入的字节数和错误
        • 示例:
          buf := bytes.NewBufferString("Hello")
          n, _ := buf.WriteTo(os.Stdout) // 输出到控制台
          fmt.Println(n) // 5
          
      • 从 Reader 读取
        • 说明:从 io.Reader 读取所有数据到缓冲区
        • 方法:ReadFrom(r io.Reader) (int64, error)
        • 返回:读取的字节数和错误
        • 示例:
          var buf bytes.Buffer
          n, _ := buf.ReadFrom(strings.NewReader("test"))
          fmt.Println(n)          // 4
          fmt.Println(buf.String()) // test
          
      • 回退字节
        • 说明:回退最后一次 ReadByte() 读取的字节
        • 方法:UnreadByte() error
        • 注意:必须在 ReadByte() 后调用,且不能连续调用
        • 示例:
          buf := bytes.NewBufferString("AB")
          b1, _ := buf.ReadByte() // 读取 A
          fmt.Printf("%c\n", b1)  // A
          buf.UnreadByte()        // 回退
          b2, _ := buf.ReadByte() // 再次读取 A
          fmt.Printf("%c\n", b2)  // A
          

  • 关闭接口

    关闭资源

    io.Closer interface
    • 定义:

      type Closer interface { Close() error }

    • 说明:

      • 用于关闭资源(文件、网络连接等)
      • 通常与 io.Reader 或 io.Writer 组合使用
      • io.ReadCloser = io.Reader + io.Closer
      • io.WriteCloser = io.Writer + io.Closer
    • 示例(完整)

      package main
      
      import (
      	"fmt"
      	"os"
      )
      
      func main() {
      	f, err := os.Create("test.txt")
      	if err != nil {
      		return
      	}
      
      	defer f.Close() // 必须关闭
      
      	fmt.Println("文件已创建")
      }
      

  • 随机读取接口

    支持从指定位置读取

    io.ReaderAt interface
    • 定义:

      type ReaderAt interface { ReadAt(p []byte, off int64) (n int, err error) }

    • 说明:

      • 从偏移量 off 开始读取 len(p) 字节
      • 不改变当前的读取位置
      • 支持并发读取(多个 goroutine 可以同时读取不同位置)
      • *os.File 实现了此接口
    • 与 io.Reader 的区别:

      • io.Read 从当前位置读取,会更新位置
      • io.ReadAt 从指定位置读取,不更新位置
    • 示例(完整)

      package main
      
      import (
      	"fmt"
      	"os"
      )
      
      func main() {
      	// 创建文件并写入数据
      	os.WriteFile("test.txt", []byte("0123456789"), 0644)
      
      	// 打开文件
      	f, _ := os.Open("test.txt")
      	defer f.Close()
      
      	// 从位置 3 开始读取 4 个字节
      	buf := make([]byte, 4)
      	n, _ := f.ReadAt(buf, 3)
      
      	fmt.Printf("读取:%s\n", string(buf)) // 3456
      	fmt.Printf("字节数:%d\n", n)
      
      	// 再次从位置 0 读取(不受上次影响)
      	f.ReadAt(buf, 0)
      	fmt.Printf("读取:%s\n", string(buf)) // 0123
      }
      

  • 随机写入接口

    支持写入到指定位置

    io.WriterAt interface
    • 定义:

      type WriterAt interface { WriteAt(p []byte, off int64) (n int, err error) }

    • 说明:

      • 从偏移量 off 开始写入数据
      • 不改变当前的写入位置
      • 支持并发写入(需要注意数据竞争)
      • *os.File 实现了此接口
    • 与 io.Writer 的区别:

      • io.Write 追加到当前位置,会更新位置
      • io.WriteAt 写入到指定位置,不更新位置
    • 示例(完整)

      package main
      
      import (
      	"fmt"
      	"os"
      )
      
      func main() {
      	// 创建文件
      	f, _ := os.Create("test.dat")
      	defer f.Close()
      
      	// 在位置 0 写入
      	f.WriteAt([]byte("Hello"), 0)
      
      	// 在位置 6 写入
      	f.WriteAt([]byte("World"), 6)
      
      	// 在位置 5 写入(填充中间)
      	f.WriteAt([]byte(" "), 5)
      
      	// 读取查看结果
      	data, _ := os.ReadFile("test.dat")
      	fmt.Printf("文件内容:%s\n", string(data)) // Hello World
      }
      

  • 定位接口

    支持移动读写位置

    io.Seeker interface
    • 定义:

      type Seeker interface { Seek(offset int64, whence int) (int64, error) }

    • 说明:

      • 移动文件的读写位置
      • offset:偏移量
      • whence:起始位置
        • io.SeekStart:从文件开头开始计算
        • io.SeekCurrent:从当前位置开始计算
        • io.SeekEnd:从文件末尾开始计算
      • 返回新的位置
      • *os.File 实现了此接口
    • 示例(完整)

      package main
      
      import (
      	"fmt"
      	"io"
      	"os"
      )
      
      func main() {
      	// 创建文件并写入数据
      	os.WriteFile("test.txt", []byte("0123456789"), 0644)
      
      	f, _ := os.OpenFile("test.txt", os.O_RDWR, 0644)
      	defer f.Close()
      
      	// 从文件开头移动 3 个字节
      	pos, _ := f.Seek(3, io.SeekStart)
      	fmt.Printf("位置:%d\n", pos) // 3
      
      	// 从当前位置移动 2 个字节
      	pos, _ = f.Seek(2, io.SeekCurrent)
      	fmt.Printf("位置:%d\n", pos) // 5
      
      	// 从文件末尾向前移动 2 个字节
      	pos, _ = f.Seek(-2, io.SeekEnd)
      	fmt.Printf("位置:%d\n", pos) // 8
      
      	// 读取当前位置的数据
      	buf := make([]byte, 2)
      	f.Read(buf)
      	fmt.Printf("读取:%s\n", string(buf)) // 89
      }
      

  • 读取到 Writer 接口

    可将自身内容写入到 io.Writer

    io.WriterTo interface
    • 定义:

      type WriterTo interface { WriteTo(w Writer) (n int64, err error) }

    • 说明:

      • 将对象的内容写入到另一个 Writer
      • 返回写入的字节数和错误
      • io.Copy 会优先使用此接口进行优化
      • *bytes.Buffer、*strings.Reader 等实现了此接口
    • 示例(完整)

      package main
      
      import (
      	"fmt"
      	"os"
      	"strings"
      )
      
      func main() {
      	// strings.Reader 实现了 io.WriterTo
      	r := strings.NewReader("Hello, WriterTo!")
      
      	// 直接写入到标准输出
      	n, _ := r.WriteTo(os.Stdout)
      
      	fmt.Printf("\n写入了 %d 字节\n", n)
      }
      

  • 从 Reader 读取接口

    可从 io.Reader 读取数据到自身

    io.ReaderFrom interface
    • 定义:

      type ReaderFrom interface { ReadFrom(r Reader) (n int64, err error) }

    • 说明:

      • 从 Reader 读取所有数据到自身
      • 返回读取的字节数和错误
      • io.Copy 会优先使用此接口进行优化
      • *bytes.Buffer 实现了此接口
    • 示例(完整)

      package main
      
      import (
      	"bytes"
      	"fmt"
      	"strings"
      )
      
      func main() {
      	// bytes.Buffer 实现了 io.ReaderFrom
      	var buf bytes.Buffer
      
      	// 从字符串读取器读取
      	r := strings.NewReader("Hello, ReaderFrom!")
      	n, _ := buf.ReadFrom(r)
      
      	fmt.Printf("读取了 %d 字节\n", n)
      	fmt.Printf("内容:%s\n", buf.String())
      }
      

  • Rune 读取接口

    支持逐 Rune(Unicode 字符)读取

    io.RuneReader interface
    • 定义:

      type RuneReader interface { ReadRune() (r rune, size int, err error) }

    • 说明:

      • 读取一个 Unicode 字符(rune)
      • 返回 rune 值、UTF-8 编码的字节数、错误
      • strings.Reader、bufio.Reader 等实现了此接口
    • 示例(完整)

      package main
      
      import (
      	"fmt"
      	"strings"
      )
      
      func main() {
      	// strings.Reader 实现了 io.RuneReader
      	r := strings.NewReader("你好 Go")
      
      	// 类型断言
      	if rr, ok := r.(interface{ ReadRune() (rune, int, error) }); ok {
      		// 读取第一个字符
      		r, size, _ := rr.ReadRune()
      		fmt.Printf("字符:%c, 字节数:%d\n", r, size) // 你,3
      
      		// 读取第二个字符
      		r, size, _ = rr.ReadRune()
      		fmt.Printf("字符:%c, 字节数:%d\n", r, size) // 好,3
      
      		// 读取空格
      		r, size, _ = rr.ReadRune()
      		fmt.Printf("字符:%c, 字节数:%d\n", r, size) // 空格,1
      	}
      }
      

  • Rune 扫描接口

    在 RuneReader 基础上支持回退

    io.RuneScanner interface
    • 定义:

      type RuneScanner interface { io.RuneReader UnreadRune() error }

    • 说明:

      • 继承 io.RuneReader
      • 支持回退最后一次读取的 rune
      • bufio.Reader 实现了此接口
    • 示例(完整)

      package main
      
      import (
      	"bufio"
      	"fmt"
      	"strings"
      )
      
      func main() {
      	// bufio.Reader 实现了 io.RuneScanner
      	r := bufio.NewReader(strings.NewReader("ABC"))
      
      	// 读取一个字符
      	ch, _, _ := r.ReadRune()
      	fmt.Printf("%c\n", ch) // A
      
      	// 回退
      	r.UnreadRune()
      
      	// 再次读取(还是 A)
      	ch, _, _ = r.ReadRune()
      	fmt.Printf("%c\n", ch) // A
      }
      

  • 读写关闭组合接口

    读取 + 关闭

    io.ReadCloser interface
    • 定义:

      type ReadCloser interface { Reader Closer }

    • 说明:

      • 组合接口:io.Reader + io.Closer
      • 用于可读取且需要关闭的资源
      • http.Response.Body、os.File 等实现了此接口
    • 示例

      var rc io.ReadCloser = os.Stdin
      // 使用
      buf := make([]byte, 10)
      rc.Read(buf)
      rc.Close()
      

  • 写关闭组合接口

    写入 + 关闭

    io.WriteCloser interface
    • 定义:

      type WriteCloser interface { Writer Closer }

    • 说明:

      • 组合接口:io.Writer + io.Closer
      • 用于可写入且需要关闭的资源
      • 压缩写入器(gzip.Writer)等实现了此接口
    • 示例

      var wc io.WriteCloser = gzip.NewWriter(file)
      // 使用
      wc.Write([]byte("data"))
      wc.Close()
      

  • 读写组合接口

    读取 + 写入

    io.ReadWriter interface
    • 定义:

      type ReadWriter interface { Reader Writer }

    • 说明:

      • 组合接口:io.Reader + io.Writer
      • 用于既可读又可写的资源
      • net.Conn、*bytes.Buffer 等实现了此接口
    • 示例

      var rw io.ReadWriter = &bytes.Buffer{}
      rw.Write([]byte("hello"))
      rw.Read(make([]byte, 5))
      

  • 读写关闭组合接口

    读取 + 写入 + 关闭

    io.ReadWriteCloser interface
    • 定义:

      type ReadWriteCloser interface { Reader Writer Closer }

    • 说明:

      • 组合接口:io.Reader + io.Writer + io.Closer
      • 用于全功能的流式资源
      • net.Conn 等实现了此接口
    • 示例

      var rwc io.ReadWriteCloser = conn
      rwc.Write([]byte("request"))
      rwc.Read(make([]byte, 100))
      rwc.Close()
      

  • 读写定位组合接口

    读取 + 写入 + 定位

    io.ReadWriteSeeker interface
    • 定义:

      type ReadWriteSeeker interface { Reader Writer Seeker }

    • 说明:

      • 组合接口:io.Reader + io.Writer + io.Seeker
      • 用于支持随机访问的文件
      • *os.File 实现了此接口
    • 示例

      var rws io.ReadWriteSeeker = file
      rws.Write([]byte("hello"))
      rws.Seek(0, io.SeekStart)
      rws.Read(make([]byte, 5))
      

  • 数据拷贝

    从 src 拷贝数据到 dst(直到 EOF)

    io.Copy(dst Writer, src Reader) (written int64, err error)
    • 说明:
      • 将 src 的所有数据拷贝到 dst
      • 直到 src 返回 EOF 或发生错误
      • 使用 32KB 的内部缓冲区
      • 如果 src 实现了 WriterTo,会调用 src.WriteTo
      • 如果 dst 实现了 ReaderFrom,会调用 dst.ReadFrom
    • 返回值:
      • written:拷贝的总字节数
      • err:遇到的错误(EOF 除外)
    • 示例(完整)
      package main
      
      import (
      	"fmt"
      	"io"
      	"strings"
      )
      
      func main() {
      	src := strings.NewReader("hello world")
      	dst := &strings.Builder{}
      
      	n, _ := io.Copy(dst, src)
      
      	fmt.Println("写入字节:", n)
      	fmt.Println("结果:", dst.String())
      }
      
    • 使用场景示例
      • 文件拷贝
        • 示例:
          src, _ := os.Open("source.txt")
          defer src.Close()
          dst, _ := os.Create("dest.txt")
          defer dst.Close()
          io.Copy(dst, src)
          
      • HTTP 响应保存
        • 示例:
          resp, _ := http.Get("http://example.com")
          defer resp.Body.Close()
          file, _ := os.Create("page.html")
          defer file.Close()
          io.Copy(file, resp.Body)
          
      • 标准输入到标准输出
        • 示例:
          io.Copy(os.Stdout, os.Stdin)
          

  • 使用缓冲区拷贝

    使用自定义缓冲区拷贝

    io.CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error)
    • 说明:
      • 与 io.Copy 类似,但使用提供的缓冲区
      • 可以控制缓冲区大小以优化性能
      • 如果 buf 为 nil,会使用 io.Copy 的默认 32KB 缓冲区
    • 使用场景:
      • 需要控制内存使用时
      • 需要优化特定场景性能时
      • 需要避免大缓冲区时
    • 示例(完整)
      package main
      
      import (
      	"fmt"
      	"io"
      	"strings"
      )
      
      func main() {
      	// 使用小缓冲区(4 字节)
      	buf := make([]byte, 4)
      
      	src := strings.NewReader("hello world")
      	dst := &strings.Builder{}
      
      	n, _ := io.CopyBuffer(dst, src, buf)
      
      	fmt.Println("写入字节:", n)
      	fmt.Println("结果:", dst.String())
      }
      
    • 性能对比示例
      • 小缓冲区(多次系统调用)
        • 示例:
          buf := make([]byte, 64) // 64 字节
          io.CopyBuffer(dst, src, buf)
          
      • 大缓冲区(少次系统调用)
        • 示例:
          buf := make([]byte, 32*1024) // 32KB
          io.CopyBuffer(dst, src, buf)
          

  • 拷贝指定字节数

    拷贝指定字节数

    io.CopyN(dst Writer, src Reader, n int64) (written int64, err error)
    • 说明:
      • 从 src 拷贝恰好 n 字节到 dst
      • 如果数据不足 n 字节,返回错误
      • 使用内部的缓冲区进行拷贝
    • 返回值:
      • written:实际拷贝的字节数
      • err:如果数据不足,返回 ErrUnexpectedEOF
    • 示例(完整)
      package main
      
      import (
      	"fmt"
      	"io"
      	"strings"
      )
      
      func main() {
      	src := strings.NewReader("hello world")
      	dst := &strings.Builder{}
      
      	// 只拷贝 5 字节
      	n, err := io.CopyN(dst, src, 5)
      	if err != nil {
      		fmt.Println("错误:", err)
      		return
      	}
      
      	fmt.Println("写入字节:", n)
      	fmt.Println("结果:", dst.String())
      }
      
    • 错误情况示例
      • 数据不足
        • 示例:
          src := strings.NewReader("hi")
          dst := &strings.Builder{}
          
          n, err := io.CopyN(dst, src, 5)
          fmt.Println(n)   // 2
          fmt.Println(err) // unexpected EOF
          
      • 从文件读取前 N 字节
        • 示例:
          file, _ := os.Open("data.bin")
          defer file.Close()
          
          dst := &bytes.Buffer{}
          io.CopyN(dst, file, 1024) // 只读取前 1KB
          

  • 丢弃写入

    一个“黑洞“写入器(数据会被丢弃)

    io.Discard (Writer)
    • 说明:
      • 实现了 io.Writer 接口
      • 所有写入的数据都会被丢弃
      • Write 方法总是返回成功
      • 类似 Unix 的 /dev/null
    • 用途:
      • 忽略不需要的输出
      • 测试性能(测量最大吞吐量)
      • 作为占位符 Writer
    • 示例(完整)
      package main
      
      import (
      	"fmt"
      	"io"
      	"strings"
      )
      
      func main() {
      	src := strings.NewReader("hello")
      
      	// 丢弃所有数据
      	n, _ := io.Copy(io.Discard, src)
      
      	fmt.Println("丢弃了", n, "字节")
      }
      
    • 使用场景示例
      • 忽略 HTTP 响应体
        • 示例:
          resp, _ := http.Get("http://example.com")
          defer resp.Body.Close()
          io.Copy(io.Discard, resp.Body) // 忽略响应体
          
      • 性能测试
        • 示例:
          // 测试最大读取速度
          data := make([]byte, 1024*1024)
          src := bytes.NewReader(data)
          io.Copy(io.Discard, src)
          
      • 跳过错误输出
        • 示例:
          cmd := exec.Command("noisy-command")
          cmd.Stdout = io.Discard // 忽略标准输出
          cmd.Stderr = io.Discard // 忽略错误输出
          cmd.Run()
          

🔥 总结

  • ByteReader 👉 逐字节读取
  • ByteScanner 👉 可回退读取
  • ByteWriter 👉 逐字节写入
  • Closer 👉 关闭资源

👉 拷贝函数:

  • Copy 👉 全量拷贝
  • CopyBuffer 👉 自定义缓冲
  • CopyN 👉 指定长度拷贝
  • Discard 👉 丢弃输出

Go语言标准库 —— io 包(错误变量)


🔹 错误变量(error)

👉 以下均为 io 包中定义的标准错误

👉 推荐写法:

if err == io.EOF { ... }

  • 文件结束错误

    表示读取到文件末尾(最常用)

    var EOF = errors.New("EOF")
    • 说明:
      • 表示读取操作已到达文件末尾
      • 这是正常情况,不是真正的错误
      • 读取循环应该检查这个错误并正常退出
    • 示例(完整)
      package main
      
      import (
      	"fmt"
      	"io"
      	"strings"
      )
      
      func main() {
      	r := strings.NewReader("hello")
      	buf := make([]byte, 10)
      
      	for {
      		n, err := r.Read(buf)
      		if err == io.EOF {
      			fmt.Println("读取完成")
      			break
      		}
      		if err != nil {
      			fmt.Println("错误:", err)
      			return
      		}
      		fmt.Printf("读取:%s\n", string(buf[:n]))
      	}
      }
      

  • 意外结束错误

    表示在读取完整数据前意外到达文件末尾

    var ErrUnexpectedEOF = errors.New("unexpected EOF")
    • 说明:
      • 与 EOF 不同,这是一个真正的错误
      • 表示期望读取更多数据,但数据源提前结束
      • 通常表示数据损坏或格式错误
    • 示例(完整)
      package main
      
      import (
      	"bytes"
      	"encoding/binary"
      	"fmt"
      	"io"
      )
      
      func main() {
      	// 期望读取一个 int32(4 字节),但只有 2 字节
      	data := []byte{1, 2}
      	r := bytes.NewReader(data)
      
      	var value int32
      	err := binary.Read(r, binary.LittleEndian, &value)
      	if err == io.ErrUnexpectedEOF {
      		fmt.Println("数据不完整:", err)
      	}
      }
      

  • 短写入错误

    表示只写入了部分数据

    var ErrShortWrite = errors.New("short write")
    • 说明:
      • 表示写入操作没有写入所有数据
      • 通常发生在写入固定大小的存储时
    • 示例(完整)
      package main
      
      import (
      	"fmt"
      	"io"
      )
      
      // 自定义 Writer,只接受部分数据
      type LimitedWriter struct {
      	Remaining int
      }
      
      func (w *LimitedWriter) Write(p []byte) (int, error) {
      	if len(p) > w.Remaining {
      		w.Remaining = 0
      		return w.Remaining, io.ErrShortWrite
      	}
      	w.Remaining -= len(p)
      	return len(p), nil
      }
      
      func main() {
      	lw := &LimitedWriter{Remaining: 5}
      	n, err := lw.Write([]byte("hello world"))
      	if err == io.ErrShortWrite {
      		fmt.Printf("只写入了 %d 字节,错误:%v\n", n, err)
      	}
      }
      

  • 短缓冲区错误

    表示提供的缓冲区太小

    var ErrShortBuffer = errors.New("short buffer")
    • 说明:
      • 表示提供的缓冲区不足以容纳数据
      • 通常发生在读取操作需要最小缓冲区时
    • 示例(完整)
      package main
      
      import (
      	"bytes"
      	"fmt"
      	"io"
      )
      
      func main() {
      	// ReadAtLeast 需要至少读取 10 字节
      	r := &bytes.Reader{}
      	buf := make([]byte, 5) // 但缓冲区只有 5 字节
      
      	_, err := io.ReadAtLeast(r, buf, 10)
      	if err == io.ErrShortBuffer {
      		fmt.Println("缓冲区太小:", err)
      	}
      }
      

  • 管道关闭错误

    表示在已关闭的管道上执行操作

    var ErrClosedPipe = errors.New("io: read/write on closed pipe")
    • 说明:
      • 发生在 io.Pipe 的读取端或写入端已关闭后
      • 尝试在关闭的管道上读写会返回此错误
    • 示例(完整)
      package main
      
      import (
      	"fmt"
      	"io"
      )
      
      func main() {
      	r, w := io.Pipe()
      	w.Close() // 先关闭写入端
      
      	buf := make([]byte, 10)
      	_, err := r.Read(buf)
      	if err == io.ErrClosedPipe {
      		fmt.Println("管道已关闭:", err)
      	}
      }
      

  • 无进展错误

    表示读取器长时间没有进展

    var ErrNoProgress = errors.New("io: read/write on closed pipe")
    • 说明:
      • 用于检测读取器长时间没有返回数据也没有返回错误的情况
      • 通常用于包装读取器进行超时检测
    • 示例
      // 这个错误较少直接使用
      // 通常由 io.NoProgressTimeout 等机制触发
      if err == io.ErrNoProgress {
      	fmt.Println("读取无进展")
      }
      

🔹 Error() 方法说明

👉 所有 error 都实现:

err.Error()

👉 一般无需手动调用(fmt 会自动调用)


🔥 总结

  • EOF 👉 正常结束(最重要)
  • ErrUnexpectedEOF 👉 意外结束(真正错误)
  • ErrShortWrite 👉 写入不完整
  • ErrShortBuffer 👉 缓冲区太小
  • ErrClosedPipe 👉 管道已关闭
  • ErrNoProgress 👉 读取无进展

👉 判断错误统一写法:

if err == io.EOF {
    // 正常结束
}

if err == io.ErrUnexpectedEOF {
    // 数据损坏
}

if err == io.ErrShortWrite {
    // 写入不完整
}

Go语言标准库 —— io 包(组合 Reader / Writer)


  • 限制读取

    io.LimitReader(r Reader, n int64) Reader

    返回一个最多读取 n 字节的 Reader。
    • 说明:
      • 超过 n 后返回 EOF
    • 示例(完整)
      package main
      
      import (
      	"fmt"
      	"io"
      	"strings"
      )
      
      func main() {
      	r := strings.NewReader("hello world")
      
      	lr := io.LimitReader(r, 5)
      
      	buf := make([]byte, 10)
      	n, _ := lr.Read(buf)
      
      	fmt.Println(string(buf[:n])) // hello
      }
      

  • 限制读取结构体

    限制读取的底层实现

    io.LimitedReader struct
    • 字段:
      • R Reader
      • N int64 // 剩余可读字节数
    • 说明:
      • 包装一个 io.Reader,限制最多读取 N 字节
      • 当 N <= 0 时,Read 会返回 EOF
      • 每次读取后会自动减少 N 的值
    • 常用方法详解
      • Read 方法
        • 说明:读取数据,最多读取 N 字节
        • 方法:Read(p []byte) (n int, err error)
        • 注意:读取后 N 会减少相应的字节数
        • 示例:
          lr := &io.LimitedReader{
          	R: strings.NewReader("hello world"),
          	N: 5,
          }
          
          buf := make([]byte, 10)
          n, _ := lr.Read(buf)
          
          fmt.Println(n)          // 5
          fmt.Println(string(buf[:n])) // hello
          fmt.Println(lr.N)       // 0(剩余 0 字节)
          
      • 再次读取(N=0 时)
        • 说明:当 N=0 时,Read 会立即返回 EOF
        • 示例:
          lr := &io.LimitedReader{
          	R: strings.NewReader("hello"),
          	N: 0,
          }
          
          buf := make([]byte, 5)
          n, err := lr.Read(buf)
          
          fmt.Println(n)   // 0
          fmt.Println(err) // io.EOF
          
    • 示例(完整)
      package main
      
      import (
      	"fmt"
      	"io"
      	"strings"
      )
      
      func main() {
      	lr := &io.LimitedReader{
      		R: strings.NewReader("hello world"),
      		N: 5,
      	}
      
      	// 第一次读取
      	buf := make([]byte, 10)
      	n, err := lr.Read(buf)
      	fmt.Printf("读取:%s, 剩余:%d, 错误:%v\n", string(buf[:n]), lr.N, err)
      
      	// 第二次读取(N=0,返回 EOF)
      	n, err = lr.Read(buf)
      	fmt.Printf("读取:%d, 错误:%v\n", n, err)
      }
      

  • 多 Reader 合并

    将多个 Reader 串联

    io.MultiReader(readers ...Reader) Reader
    • 说明:
      • 将多个 io.Reader 串联成一个 io.Reader
      • 按顺序读取每个 Reader,直到所有 Reader 都返回 EOF
      • 返回的 Reader 实现了 io.Reader 接口
    • 工作原理:
      • 先读取第一个 Reader,直到 EOF
      • 然后自动切换到下一个 Reader
      • 所有 Reader 都读完才返回 EOF
    • 示例(完整)
      package main
      
      import (
      	"fmt"
      	"io"
      	"strings"
      )
      
      func main() {
      	r1 := strings.NewReader("hello ")
      	r2 := strings.NewReader("world")
      	r3 := strings.NewReader("!")
      
      	// 串联多个 Reader
      	r := io.MultiReader(r1, r2, r3)
      
      	buf := make([]byte, 20)
      	n, _ := r.Read(buf)
      
      	fmt.Println(string(buf[:n])) // hello world!
      }
      
    • 使用场景示例
      • 合并多个文件
        • 示例:
          f1, _ := os.Open("part1.txt")
          f2, _ := os.Open("part2.txt")
          f3, _ := os.Open("part3.txt")
          
          r := io.MultiReader(f1, f2, f3)
          io.Copy(os.Stdout, r)
          
      • 添加文件头尾
        • 示例:
          header := strings.NewReader("{ \"data\": [")
          footer := strings.NewReader("] }")
          
          r := io.MultiReader(header, fileReader, footer)
          data, _ := io.ReadAll(r)
          
      • 动态添加 Reader
        • 示例:
          readers := []io.Reader{
          	strings.NewReader("start"),
          	file,
          	strings.NewReader("end"),
          }
          r := io.MultiReader(readers...)
          

  • 多 Writer 写入

    将数据同时写入多个 Writer

    io.MultiWriter(writers ...Writer) Writer
    • 说明:
      • 将多个 io.Writer 合并成一个 io.Writer
      • 每次 Write 会写入到所有 Writer
      • 返回的 Writer 实现了 io.Writer 接口
    • 工作原理:
      • 调用 Write 时,按顺序写入每个 Writer
      • 如果某个 Writer 返回错误,立即停止并返回该错误
      • 所有 Writer 都成功才返回成功
    • 示例(完整)
      package main
      
      import (
      	"bytes"
      	"fmt"
      	"io"
      	"os"
      )
      
      func main() {
      	var buf bytes.Buffer
      
      	// 同时写入 stdout 和 buffer
      	w := io.MultiWriter(os.Stdout, &buf)
      
      	w.Write([]byte("hello\n"))
      
      	fmt.Println("缓冲区内容:", buf.String())
      }
      
    • 使用场景示例
      • 日志输出到文件和控制台
        • 示例:
          logFile, _ := os.Create("app.log")
          defer logFile.Close()
          
          w := io.MultiWriter(os.Stdout, logFile)
          logger := log.New(w, "", 0)
          logger.Println("启动服务")
          
      • 数据备份
        • 示例:
          var backup bytes.Buffer
          w := io.MultiWriter(destination, &backup)
          io.Copy(w, source)
          // 此时 destination 和 backup 都有相同数据
          
      • 写入多个文件
        • 示例:
          f1, _ := os.Create("copy1.txt")
          f2, _ := os.Create("copy2.txt")
          defer f1.Close()
          defer f2.Close()
          
          w := io.MultiWriter(f1, f2)
          w.Write([]byte("duplicate"))
          

  • 偏移写入器

    创建带偏移的 Writer

    io.NewOffsetWriter(w WriterAt, off int64) *io.OffsetWrite
    • 说明:
      • 创建一个从指定偏移量开始写入的 Writer
      • 内部维护一个偏移量计数器
      • 每次 Write 后自动更新偏移量
      • 返回的 OffsetWriter 实现了 io.Writer 和 io.WriterAt 接口
    • 使用场景:
      • 在文件的特定位置开始写入
      • 跳过文件头部(如保留头部空间)
      • 连续写入到不同位置
    • 示例(完整)
      package main
      
      import (
      	"fmt"
      	"io"
      	"os"
      )
      
      func main() {
      	f, _ := os.Create("test.txt")
      	defer f.Close()
      
      	// 从位置 5 开始写入
      	w := io.NewOffsetWriter(f, 5)
      
      	w.Write([]byte("hello")) // 写入到位置 5-9
      	w.Write([]byte(" "))     // 写入到位置 10
      	w.Write([]byte("world")) // 写入到位置 11-15
      
      	fmt.Println("写入完成")
      
      	// 读取查看结果
      	data, _ := os.ReadFile("test.txt")
      	fmt.Printf("文件内容:%q\n", string(data))
      }
      
    • 使用场景示例
      • 跳过文件头部
        • 示例:
          file, _ := os.Create("data.bin")
          // 跳过前 128 字节(保留给头部)
          w := io.NewOffsetWriter(file, 128)
          w.Write(data)
          // 然后再写入头部
          file.WriteAt(header, 0)
          
      • 连续写入
        • 示例:
          file, _ := os.OpenFile("log.bin", os.O_RDWR|os.O_CREATE, 0644)
          w := io.NewOffsetWriter(file, 0)
          
          // 每次写入自动更新偏移量
          w.Write(record1)
          w.Write(record2)
          w.Write(record3)
          

  • 分段读取器

    读取指定区间的数据

    io.NewSectionReader(r ReaderAt, off int64, n int64) *io.SectionReader
    • 示例(完整)
      package main
      
      import (
      	"fmt"
      	"io"
      	"strings"
      )
      
      func main() {
      	r := strings.NewReader("hello world")
      
      	sr := io.NewSectionReader(r, 6, 5)
      
      	buf := make([]byte, 5)
      	sr.Read(buf)
      
      	fmt.Println(string(buf)) // world
      }
      

  • 空关闭包装

    将 Reader 包装为 ReadCloser(无实际关闭)

    io.NopCloser(r Reader) ReadCloser
    • 说明:
      • 将一个 io.Reader 包装成 io.ReadCloser
      • Close 方法什么都不做(no-op)
      • 用于需要 ReadCloser 但实际不需要关闭的场景
    • 使用场景:
      • 函数签名需要 io.ReadCloser,但数据源不需要关闭
      • 测试代码中模拟 io.ReadCloser
      • 包装内存数据源
    • 示例(完整)
      package main
      
      import (
      	"fmt"
      	"io"
      	"strings"
      )
      
      func main() {
      	r := strings.NewReader("hello")
      
      	// 包装为 ReadCloser
      	rc := io.NopCloser(r)
      
      	// 正常读取
      	buf := make([]byte, 5)
      	rc.Read(buf)
      	fmt.Println(string(buf))
      
      	// Close 什么都不做
      	rc.Close()
      }
      
    • 使用场景示例
      • 满足接口要求
        • 示例:
          func Process(r io.ReadCloser) {
          	defer r.Close()
          	// 处理...
          }
          
          // 使用 NopCloser 传递字符串
          Process(io.NopCloser(strings.NewReader("data")))
          
      • HTTP 测试
        • 示例:
          // 模拟 HTTP 响应体
          resp := &http.Response{
          	Body: io.NopCloser(strings.NewReader("test")),
          }
          
      • 包装 bytes.Buffer
        • 示例:
          var buf bytes.Buffer
          buf.WriteString("data")
          rc := io.NopCloser(&buf)
          // 现在 rc 实现了 io.ReadCloser
          

  • 偏移写入器结构体

    带偏移的写入器

    io.OffsetWriter struct
    • 说明:
      • 实现了 io.Writer 和 io.WriterAt 接口
      • 内部维护当前的偏移量
      • 通常通过 io.NewOffsetWriter 创建
    • 常用方法详解
      • Write 方法
        • 说明:在当前偏移量位置写入数据
        • 方法:Write(p []byte) (n int, err error)
        • 注意:写入后会自动更新偏移量
        • 示例:
          f, _ := os.Create("file.txt")
          w := io.NewOffsetWriter(f, 0)
          
          w.Write([]byte("hello")) // 偏移量变为 5
          w.Write([]byte(" "))     // 偏移量变为 6
          w.Write([]byte("world")) // 偏移量变为 11
          
      • WriteAt 方法
        • 说明:在指定偏移量位置写入数据
        • 方法:WriteAt(p []byte, off int64) (n int, err error)
        • 注意:会更新内部偏移量为 off + int64(len(p))
        • 示例:
          f, _ := os.Create("file.txt")
          w := io.NewOffsetWriter(f, 0)
          
          w.WriteAt([]byte("hello"), 10) // 在位置 10 写入
          // 内部偏移量现在是 15
          
    • 示例(完整)
      package main
      
      import (
      	"fmt"
      	"io"
      	"os"
      )
      
      func main() {
      	f, _ := os.Create("file.txt")
      	defer f.Close()
      
      	// 从位置 2 开始
      	w := io.NewOffsetWriter(f, 2)
      
      	// 普通写入(从位置 2 开始)
      	w.Write([]byte("ABC"))
      
      	// 指定位置写入
      	w.WriteAt([]byte("X"), 0) // 在位置 0 写入
      
      	fmt.Println("写入完成")
      
      	// 读取查看结果
      	data, _ := os.ReadFile("file.txt")
      	fmt.Printf("文件内容:%q\n", string(data))
      }
      

🔥 总结

  • LimitReader 👉 限制读取长度
  • LimitedReader 👉 限制读取结构体
  • MultiReader 👉 多输入流合并
  • MultiWriter 👉 多输出流写入
  • NewOffsetWriter 👉 偏移写入
  • NewSectionReader 👉 区间读取
  • NopCloser 👉 包装关闭接口
  • OffsetWriter 👉 偏移写入结构体

Go语言标准库 —— io 包(Pipe & Reader接口族)


  • 管道(内存同步)

    创建一个同步内存管道(读写阻塞)

    io.Pipe() (*io.PipeReader, *io.PipeWriter)
    • 说明:
      • 写入端写数据 → 读端才能读取
      • 常用于 goroutine 通信
    • 示例(完整)
      package main
      
      import (
      	"fmt"
      	"io"
      )
      
      func main() {
      	r, w := io.Pipe()
      
      	go func() {
      		w.Write([]byte("hello pipe"))
      		w.Close()
      	}()
      
      	buf := make([]byte, 20)
      	n, _ := r.Read(buf)
      
      	fmt.Println(string(buf[:n]))
      }
      

  • 管道读取端

    管道读取端结构体

    io.PipeReader struct
    • 说明:
      • 实现了 io.Reader 接口
      • 与 PipeWriter 配对使用
      • 读取操作会阻塞,直到有数据写入
    • 常用方法详解
      • Read 方法
        • 说明:从管道读取数据
        • 方法:Read(p []byte) (n int, err error)
        • 注意:如果写入端未写入数据,会阻塞等待
        • 示例:
          r, w := io.Pipe()
          
          go func() {
          	w.Write([]byte("hello"))
          	w.Close()
          }()
          
          buf := make([]byte, 10)
          n, err := r.Read(buf)
          fmt.Println(string(buf[:n])) // hello
          
      • Close 方法
        • 说明:关闭读取端
        • 方法:Close() error
        • 注意:关闭后写入端会收到 ErrClosedPipe 错误
        • 示例:
          r, w := io.Pipe()
          r.Close() // 关闭读取端
          
          _, err := w.Write([]byte("data"))
          fmt.Println(err) // io: read/write on closed pipe
          
      • CloseWithError 方法
        • 说明:关闭读取端并返回指定错误
        • 方法:CloseWithError(err error) error
        • 注意:写入端会收到这个错误
        • 示例:
          r, w := io.Pipe()
          
          go func() {
          	r.CloseWithError(fmt.Errorf("自定义错误"))
          }()
          
          _, err := w.Write([]byte("data"))
          fmt.Println(err) // 自定义错误
          
    • 示例(完整)
      package main
      
      import (
      	"fmt"
      	"io"
      )
      
      func main() {
      	r, w := io.Pipe()
      
      	// 写入端在另一个 goroutine 中
      	go func() {
      		w.Write([]byte("hello"))
      		w.Write([]byte(" "))
      		w.Write([]byte("pipe"))
      		w.Close()
      	}()
      
      	// 读取端
      	buf := make([]byte, 20)
      	total := 0
      	for {
      		n, err := r.Read(buf)
      		if err == io.EOF {
      			break
      		}
      		if err != nil {
      			fmt.Println("错误:", err)
      			return
      		}
      		total += n
      	}
      
      	fmt.Println("读取完成,总字节:", total)
      }
      

  • 管道写入端

    管道写入端结构体

    io.PipeWriter struct
    • 说明:
      • 实现了 io.Writer 接口
      • 与 PipeReader 配对使用
      • 写入操作会阻塞,直到读取端读取数据
    • 常用方法详解
      • Write 方法
        • 说明:向管道写入数据
        • 方法:Write(p []byte) (n int, err error)
        • 注意:如果读取端未读取,会阻塞等待
        • 示例:
          r, w := io.Pipe()
          
          go func() {
          	w.Write([]byte("hello"))
          	w.Close()
          }()
          
          data, _ := io.ReadAll(r)
          fmt.Println(string(data)) // hello
          
      • Close 方法
        • 说明:关闭写入端
        • 方法:Close() error
        • 注意:关闭后读取端会收到 EOF
        • 示例:
          r, w := io.Pipe()
          
          go func() {
          	w.Write([]byte("data"))
          	w.Close() // 关闭写入端
          }()
          
          data, _ := io.ReadAll(r)
          fmt.Println(string(data)) // data
          
      • CloseWithError 方法
        • 说明:关闭写入端并返回指定错误给读取端
        • 方法:CloseWithError(err error) error
        • 注意:读取端会收到这个错误而不是 EOF
        • 示例:
          r, w := io.Pipe()
          
          go func() {
          	w.CloseWithError(fmt.Errorf("写入失败"))
          }()
          
          _, err := io.ReadAll(r)
          fmt.Println(err) // 写入失败
          
    • 示例(完整)
      package main
      
      import (
      	"fmt"
      	"io"
      )
      
      func main() {
      	r, w := io.Pipe()
      
      	// 写入端
      	go func() {
      		// 写入多批数据
      		w.Write([]byte("line1\n"))
      		w.Write([]byte("line2\n"))
      		w.Write([]byte("line3\n"))
      
      		// 正常关闭
      		w.Close()
      	}()
      
      	// 读取端
      	data, err := io.ReadAll(r)
      	if err != nil {
      		fmt.Println("错误:", err)
      		return
      	}
      
      	fmt.Printf("读取内容:\n%s", string(data))
      }
      

  • 读取全部

    读取所有数据直到 EOF

    io.ReadAll(r Reader) ([]byte, error)
    • 说明:
      • 从 Reader 读取所有剩余数据
      • 返回读取的字节切片和错误
      • 内部会自动扩展缓冲区,无需手动分配
    • 注意事项:
      • 如果数据源很大,会占用大量内存
      • 对于大文件,建议使用 io.Copy 代替
      • 读取完成后会返回 EOF 错误(通常忽略)
    • 示例(完整)
      package main
      
      import (
      	"fmt"
      	"io"
      	"os"
      )
      
      func main() {
      	// 从文件读取所有内容
      	data, err := io.ReadAll(os.Stdin)
      	if err != nil {
      		fmt.Println("错误:", err)
      		return
      	}
      
      	fmt.Printf("读取了 %d 字节\n", len(data))
      	fmt.Printf("内容:%s\n", string(data))
      }
      
    • 从不同源读取示例
      • 从字符串读取
        • 示例:
          data, _ := io.ReadAll(strings.NewReader("hello"))
          fmt.Println(string(data)) // hello
          
      • 从 HTTP 响应读取
        • 示例:
          resp, _ := http.Get("http://example.com")
          defer resp.Body.Close()
          data, _ := io.ReadAll(resp.Body)
          fmt.Println(string(data))
          
      • 从管道读取
        • 示例:
          r, w := io.Pipe()
          go func() {
          	w.Write([]byte("hello"))
          	w.Close()
          }()
          data, _ := io.ReadAll(r)
          fmt.Println(string(data)) // hello
          

  • 至少读取 N 字节

    至少读取指定数量的字节

    io.ReadAtLeast(r Reader, buf []byte, min int) (n int, err error)
    • 说明:
      • 尝试读取至少 min 字节到 buf 中
      • 返回实际读取的字节数和错误
      • 如果 buf 长度小于 min,返回 ErrShortBuffer
    • 返回值:
      • n >= min:成功读取至少 min 字节
      • n < min 且 err == nil:读到 EOF
      • err == ErrShortBuffer:buf 太小
    • 示例(完整)
      package main
      
      import (
      	"fmt"
      	"io"
      	"strings"
      )
      
      func main() {
      	r := strings.NewReader("hello world")
      	buf := make([]byte, 10)
      
      	// 至少读取 5 字节
      	n, err := io.ReadAtLeast(r, buf, 5)
      	if err != nil {
      		fmt.Println("错误:", err)
      		return
      	}
      
      	fmt.Printf("读取了 %d 字节\n", n)
      	fmt.Printf("内容:%s\n", string(buf[:n]))
      }
      
    • 错误情况示例
      • 缓冲区太小
        • 示例:
          r := strings.NewReader("hello")
          buf := make([]byte, 3)
          
          _, err := io.ReadAtLeast(r, buf, 5)
          fmt.Println(err) // io: short buffer
          
      • 数据不足
        • 示例:
          r := strings.NewReader("hi")
          buf := make([]byte, 5)
          
          n, err := io.ReadAtLeast(r, buf, 5)
          fmt.Println(n)   // 2
          fmt.Println(err) // io: unexpected EOF
          

  • ReadCloser 接口

    io.ReadCloser interface

    • 定义:

      type ReadCloser interface { Reader Closer }

    • 示例

      rc := io.NopCloser(strings.NewReader("hello"))
      
      data, _ := io.ReadAll(rc)
      fmt.Println(string(data))
      
      rc.Close()
      

  • 读取固定长度

    必须读取完整缓冲区长度

    io.ReadFull(r Reader, buf []byte) (n int, err error)
    • 说明:
      • 必须读取恰好 len(buf) 字节
      • 如果数据不足,返回 ErrUnexpectedEOF
      • 常用于读取固定长度的数据(如二进制协议)
    • 返回值:
      • n == len(buf) 且 err == nil:成功
      • err == ErrUnexpectedEOF:数据不足
    • 示例(完整)
      package main
      
      import (
      	"fmt"
      	"io"
      	"strings"
      )
      
      func main() {
      	r := strings.NewReader("hello world")
      	buf := make([]byte, 5)
      
      	// 必须读取 5 字节
      	n, err := io.ReadFull(r, buf)
      	if err != nil {
      		fmt.Println("错误:", err)
      		return
      	}
      
      	fmt.Printf("读取了 %d 字节:%s\n", n, string(buf))
      }
      
    • 错误情况示例
      • 数据不足
        • 示例:
          r := strings.NewReader("hi")
          buf := make([]byte, 5)
          
          n, err := io.ReadFull(r, buf)
          fmt.Println(n)   // 2
          fmt.Println(err) // unexpected EOF
          
      • 读取文件头部
        • 示例:
          file, _ := os.Open("image.png")
          defer file.Close()
          
          // 读取 PNG 文件头(8 字节)
          header := make([]byte, 8)
          _, err := io.ReadFull(file, header)
          if err != nil {
          	fmt.Println("不是有效的 PNG 文件")
          }
          






  • 读取接口(核心)

    io.Reader interface

    • 定义:

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

    • 示例

      var r io.Reader = strings.NewReader("hello")
      
      buf := make([]byte, 5)
      r.Read(buf)
      
      fmt.Println(string(buf))
      

  • 随机读取

    io.ReaderAt interface

    • 定义:

      type ReaderAt interface { ReadAt(p []byte, off int64) (n int, err error) }

    • 示例

      r := strings.NewReader("hello world")
      
      buf := make([]byte, 5)
      r.ReadAt(buf, 6)
      
      fmt.Println(string(buf)) // world
      

  • ReaderFrom 接口

    io.ReaderFrom interface

    • 定义:

      type ReaderFrom interface { ReadFrom(r Reader) (n int64, err error) }

    • 说明:

      • 常用于优化 io.Copy

  • rune 读取

    io.RuneReader interface

    • 定义:

      type RuneReader interface { ReadRune() (r rune, size int, err error) }

    • 示例

      r := strings.NewReader("你好")
      
      ch, _, _ := r.ReadRune()
      fmt.Println(string(ch))
      

  • rune 扫描

    io.RuneScanner interface

    • 定义:

      type RuneScanner interface { RuneReader UnreadRune() error }

    • 示例

      r := strings.NewReader("ab")
      
      ch, _, _ := r.ReadRune()
      fmt.Println(string(ch)) // a
      
      r.UnreadRune()
      
      ch, _, _ = r.ReadRune()
      fmt.Println(string(ch)) // a
      

🔥 总结

  • Pipe 👉 内存管道通信
  • PipeReader / PipeWriter 👉 管道两端

👉 读取函数:

  • ReadAll 👉 全量读取
  • ReadFull 👉 固定读取
  • ReadAtLeast 👉 最少读取

👉 核心接口:

  • Reader 👉 最核心接口
  • Writer 👉 写入接口
  • Closer 👉 关闭接口

👉 组合接口:

  • ReadCloser / ReadWriter / ReadSeeker 等

👉 特殊接口:

  • ReaderAt 👉 随机读取
  • RuneReader 👉 Unicode读取
  • RuneScanner 👉 可回退字符

Go语言标准库 —— io 包(Seek & 高级Reader)


  • 分段读取器

    用于从 ReaderAt 中读取指定区间数据

    io.SectionReader struct
    • 字段:
      • 内部封装了 io.ReaderAt
      • 固定了起始偏移量和长度
    • 说明:
      • 允许从大的数据源中读取特定区间
      • 支持 Seek 操作,但范围限制在区间内
      • 实现了 io.Reader、io.ReaderAt、io.Seeker 接口
    • 常用方法详解
      • Read 方法
        • 说明:从当前区间位置读取数据
        • 方法:Read(p []byte) (n int, err error)
        • 注意:只能读取区间内的数据
        • 示例:
          r := strings.NewReader("hello world")
          sr := io.NewSectionReader(r, 6, 5) // 从位置 6 开始,长度 5
          
          buf := make([]byte, 5)
          n, _ := sr.Read(buf)
          
          fmt.Println(string(buf)) // world
          
      • ReadAt 方法
        • 说明:从相对于区间起始的位置读取数据
        • 方法:ReadAt(p []byte, off int64) (n int, err error)
        • 注意:off 是相对于区间起始位置(0)的偏移
        • 示例:
          r := strings.NewReader("hello world")
          sr := io.NewSectionReader(r, 6, 5) // 区间:"world"
          
          buf := make([]byte, 3)
          sr.ReadAt(buf, 2) // 从区间内位置 2 读取
          
          fmt.Println(string(buf)) // rld
          
      • Seek 方法
        • 说明:移动区间内的读取位置
        • 方法:Seek(offset int64, whence int) (int64, error)
        • 注意:位置不能超出区间范围 [0, size)
        • 示例:
          r := strings.NewReader("hello world")
          sr := io.NewSectionReader(r, 6, 5) // 区间:"world"
          
          pos, _ := sr.Seek(2, io.SeekStart)
          fmt.Println("位置:", pos) // 2
          
          buf := make([]byte, 3)
          sr.Read(buf)
          fmt.Println(string(buf)) // rld
          
      • Size 方法
        • 说明:返回区间的总长度
        • 方法:Size() int64
        • 注意:返回创建时指定的长度 n
        • 示例:
          r := strings.NewReader("hello world")
          sr := io.NewSectionReader(r, 6, 5)
          
          fmt.Println("区间大小:", sr.Size()) // 5
          
    • 示例(完整)
      package main
      
      import (
      	"fmt"
      	"io"
      	"strings"
      )
      
      func main() {
      	// 创建一个大字符串
      	r := strings.NewReader("hello world this is a test")
      
      	// 创建区间读取器:从位置 6 开始,长度 5("world")
      	sr := io.NewSectionReader(r, 6, 5)
      
      	// 方法 1:直接读取
      	buf1 := make([]byte, 5)
      	sr.Read(buf1)
      	fmt.Println("读取:", string(buf1)) // world
      
      	// 方法 2:使用 Seek 定位
      	sr.Seek(0, io.SeekStart) // 回到开头
      	buf2 := make([]byte, 3)
      	sr.Read(buf2)
      	fmt.Println("部分读取:", string(buf2)) // wor
      
      	// 方法 3:使用 ReadAt
      	buf3 := make([]byte, 2)
      	sr.ReadAt(buf3, 3) // 从区间内位置 3 读取
      	fmt.Println("ReadAt:", string(buf3)) // ld
      
      	// 查看区间大小
      	fmt.Println("区间大小:", sr.Size()) // 5
      }
      

  • Seek 常量(当前位置)

    io.SeekCurrent (int)

    相对于当前位置移动。
    • 示例
      r := strings.NewReader("hello")
      
      r.Seek(2, io.SeekStart)
      r.Seek(1, io.SeekCurrent)
      
      buf := make([]byte, 2)
      r.Read(buf)
      
      fmt.Println(string(buf)) // lo
      

  • Seek 常量(末尾)

    io.SeekEnd (int)

    相对于文件末尾移动。
    • 示例
      r := strings.NewReader("hello")
      
      r.Seek(-2, io.SeekEnd)
      
      buf := make([]byte, 2)
      r.Read(buf)
      
      fmt.Println(string(buf)) // lo
      

  • Seek 常量(开头)

    io.SeekStart (int)

    相对于起始位置移动。
    • 示例
      r := strings.NewReader("hello")
      
      r.Seek(1, io.SeekStart)
      
      buf := make([]byte, 2)
      r.Read(buf)
      
      fmt.Println(string(buf)) // el
      

  • 定位接口

    io.Seeker interface

    支持位置移动的接口。
    • 定义:

      type Seeker interface { Seek(offset int64, whence int) (int64, error) }

    • 示例

      var s io.Seeker = strings.NewReader("hello")
      
      s.Seek(2, io.SeekStart)
      
      buf := make([]byte, 3)
      s.(io.Reader).Read(buf)
      
      fmt.Println(string(buf)) // llo
      

  • 字符串写入接口

    io.StringWriter interface

    支持写入字符串(优化性能)。
    • 定义:

      type StringWriter interface { WriteString(s string) (n int, err error) }

    • 示例

      package main
      
      import (
      	"bytes"
      	"fmt"
      )
      
      func main() {
      	var buf bytes.Buffer
      
      	buf.WriteString("hello")
      	buf.WriteString(" world")
      
      	fmt.Println(buf.String())
      }
      

  • TeeReader(分流读取)

    从 r 读取的同时写入 w

    io.TeeReader(r Reader, w Writer) Reader
    • 说明:
      • 返回一个 Reader,读取时会同时写入 w
      • 类似 Linux tee 命令
      • 常用于日志、调试、数据备份
      • 返回的 Reader 实现了 io.Reader 接口
    • 工作原理:
      • 调用 Read 时,先从 r 读取数据
      • 然后将读取的数据写入 w
      • 最后返回给调用者
    • 示例(完整)
      package main
      
      import (
      	"bytes"
      	"fmt"
      	"io"
      	"strings"
      )
      
      func main() {
      	src := strings.NewReader("hello tee")
      	var buf bytes.Buffer
      
      	// 创建 TeeReader
      	tr := io.TeeReader(src, &buf)
      
      	// 读取数据(会自动写入 buf)
      	data, _ := io.ReadAll(tr)
      
      	fmt.Println("读取:", string(data))
      	fmt.Println("复制:", buf.String())
      }
      
    • 使用场景示例
      • 日志记录
        • 示例:
          file, _ := os.Create("log.txt")
          defer file.Close()
          
          tr := io.TeeReader(requestBody, file)
          data, _ := io.ReadAll(tr)
          // 此时 data 包含原始数据,file 也有备份
          
      • 数据校验
        • 示例:
          var buf bytes.Buffer
          tr := io.TeeReader(src, &buf)
          
          // 读取并处理
          data, _ := io.ReadAll(tr)
          
          // 使用 buf 中的数据进行校验
          checksum := calculateChecksum(buf.Bytes())
          
      • 调试输出
        • 示例:
          tr := io.TeeReader(src, os.Stdout)
          io.ReadAll(tr)
          // 数据既被读取,又输出到控制台
          

🔥 总结

  • SectionReader 👉 区间读取
  • SeekStart / SeekCurrent / SeekEnd 👉 定位方式
  • Seeker 👉 定位接口

👉 写入优化:

  • StringWriter 👉 字符串写入

👉 高级流:

  • TeeReader 👉 一边读一边写

Go语言标准库 —— io 包(Writer 接口族)


  • 写入关闭接口

    io.WriteCloser interface

    写入 + 关闭接口。
    • 定义:

      type WriteCloser interface { Writer Closer }

    • 示例(完整)

      package main
      
      import (
      	"fmt"
      	"io"
      	"os"
      )
      
      func main() {
      	f, err := os.Create("test.txt")
      	if err != nil {
      		return
      	}
      
      	var wc io.WriteCloser = f
      
      	wc.Write([]byte("hello"))
      	wc.Close()
      
      	fmt.Println("写入完成")
      }
      

  • 写入定位接口

    io.WriteSeeker interface

    写入 + 定位接口。
    • 定义:

      type WriteSeeker interface { Writer Seeker }

    • 示例

      package main
      
      import (
      	"fmt"
      	"os"
      )
      
      func main() {
      	f, _ := os.Create("file.txt")
      	defer f.Close()
      
      	f.Write([]byte("hello"))
      	f.Seek(0, 0)
      	f.Write([]byte("H"))
      
      	fmt.Println("覆盖写入完成")
      }
      

  • 写入字符串(函数)

    向 Writer 写入字符串

    io.WriteString(w Writer, s string) (n int, err error)
    • 说明:
      • 将字符串写入到 Writer
      • 如果 Writer 实现了 io.StringWriter,会直接调用 WriteString(避免 UTF-8 解码)
      • 否则会将字符串转换为 []byte 再调用 Write
    • 性能优化:
      • 对于实现了 io.StringWriter 的类型(如 bytes.Buffer),性能更好
      • 避免了 string -> []byte 的转换和内存分配
    • 示例(完整)
      package main
      
      import (
      	"fmt"
      	"io"
      	"os"
      )
      
      func main() {
      	// 写入到标准输出
      	io.WriteString(os.Stdout, "hello io\n")
      
      	// 写入到文件
      	file, _ := os.Create("test.txt")
      	defer file.Close()
      	io.WriteString(file, "file content\n")
      
      	// 写入到 Buffer
      	var buf bytes.Buffer
      	io.WriteString(&buf, "buffer content")
      	fmt.Println(buf.String())
      }
      
    • 使用场景示例
      • 写入多行文本
        • 示例:
          var buf bytes.Buffer
          io.WriteString(&buf, "line1\n")
          io.WriteString(&buf, "line2\n")
          io.WriteString(&buf, "line3\n")
          
      • HTTP 响应写入
        • 示例:
          w := http.ResponseWriter
          io.WriteString(w, "HTTP/1.1 200 OK\r\n")
          io.WriteString(w, "Content-Type: text/html\r\n")
          io.WriteString(w, "\r\n")
          io.WriteString(w, "<html>Hello</html>")
          
      • 条件写入
        • 示例:
          if debug {
          	io.WriteString(os.Stderr, "Debug info...\n")
          }
          

  • 写入接口(核心)

    io.Writer interface

    最基础写入接口。
    • 定义:

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

    • 示例

      var w io.Writer = os.Stdout
      
      w.Write([]byte("hello writer\n"))
      

  • 随机写入接口

    io.WriterAt interface

    支持指定位置写入。
    • 定义:

      type WriterAt interface { WriteAt(p []byte, off int64) (n int, err error) }

    • 示例

      f, _ := os.Create("file.txt")
      defer f.Close()
      
      f.WriteAt([]byte("hello"), 5)
      
      fmt.Println("随机写入完成")
      

  • WriterTo 接口

    io.WriterTo interface

    可将自身写入到 Writer。
    • 定义:

      type WriterTo interface { WriteTo(w Writer) (n int64, err error) }

    • 说明:

      • io.Copy 会优先使用该接口优化
    • 示例

      r := strings.NewReader("hello")
      
      r.WriteTo(os.Stdout)
      

🔥 总结

  • Writer 👉 核心写入接口
  • WriteCloser 👉 写入 + 关闭
  • WriteSeeker 👉 写入 + 定位
  • WriterAt 👉 随机写入

👉 工具函数:

  • WriteString 👉 写字符串(优化)

👉 高级接口:

  • WriterTo 👉 主动写出(优化 io.Copy)