io 标准库中核心是要理解 io.Readerio.Writer 两个接口

// Reader 其实就是封装了下Read函数
// Read函数期望读取 len(p) bytes到 p 中,并返回实际读取的长度,即使数据不足 len(p) 
// 也会立即返回;只要读取到的数据长度大于0,就要返回 err = nil; 如果没有可读取的数据,则返回 0, EOF; Read不允许出现 0,nil 这样的返回值
type Reader interface {
    Read(p []byte) (n int, err error)
}

// Reader 其实是封装了下Writer函数
// Write 就是将 p 中保存的数据写入到内部结构体,并返回实际写入的数据
// 同Read一样,返回值不能是 0, nil; 并且Write函数中不能改变原始数据,哪怕临时改变也不允许
type Writer interface {
    Write(p []byte) (n int, err error)
}

围绕io.Reader/Writer,有几个常用的实现:

  • net.Conn, os.Stdin, os.File: 网络、标准输入输出、文件的流读取
  • strings.Reader: 把字符串抽象成Reader
  • bytes.Reader: 把[]byte抽象成Reader
  • bytes.Buffer: 把[]byte抽象成Reader和Writer
  • bufio.Reader/Writer: 抽象成带缓冲的流读取(比如按行读写)
    strings.Reader 源码举例分析:

    // 当然 string.Reader 不仅仅实现了io.Reader接口,还支持很多
    // A Reader implements the io.Reader, io.ReaderAt, io.ByteReader, io.ByteScanner,
    // io.RuneReader, io.RuneScanner, io.Seeker, and io.WriterTo interfaces by reading
    // from a string.
    // The zero value for Reader operates like a Reader of an empty string.
    type Reader struct {
      s        string
      i        int64 // 指向现在读取的位置
      prevRune int   // index of previous rune; or < 0
    }
    
    // Read implements the io.Reader interface.
    func (r *Reader) Read(b []byte) (n int, err error) {
      // 已经读到字符串末尾,直接返回 io.EOF
      if r.i >= int64(len(r.s)) {
          return 0, io.EOF
      }
       // 因为是按照byte读的,所以这个标志位无意义
      r.prevRune = -1
      // 读取数据
      n = copy(b, r.s[r.i:])
      // 移动读取位置指针,向后偏移实际读取的量
      r.i += int64(n)
      return
    }

    当很多io相关的操作,比如读写文件、读写网络数据等场景都实现了 Write、Read 接口,那么很多跨io的操作就可以无缝完成,比如将网络读取的数据直接写入文件

    // Copy 从 src 拷贝数据到 dst,直到遇到 EOF,或者发生错误
    // 如果 src 中实现了 WriterTo 接口,则调用 src.WriteTo(dst)
    // 如果 dst 中实现了 ReaderFrom 接口,则调用 dst.ReadFrom(src)
    func Copy(dst Writer, src Reader) (written int64, err error) {
      return copyBuffer(dst, src, nil)
    }
    // 这个函数与Copy的区别就是需要我们自己提供buf缓冲区
    func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
      if buf != nil && len(buf) == 0 {
          panic("empty buffer in CopyBuffer")
      }
      return copyBuffer(dst, src, buf)
    }
    
    // copyBuffer 是 Copy 和 CopyBuffer 的具体实现
    func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
      // 如果 Reader 实现了 WriteTo 方法, 则直接调用它是先数据拷贝
      if wt, ok := src.(WriterTo); ok {
          return wt.WriteTo(dst)
      }
      // 如果 Writer 实现了 ReaderFrom 方法, 则直接调用它是先数据拷贝
      if rt, ok := dst.(ReaderFrom); ok {
          return rt.ReadFrom(src)
      }
      // 如果没有提供数据缓冲去,则分配一段临时缓冲区
      if buf == nil {
          size := 32 * 1024
          // LimitedReader 是io中实现的一个有读取长度限制的结构体,所以如果Read中实现了该结构体,则使用其定义的长度
          if l, ok := src.(*LimitedReader); ok && int64(size) > l.N {
              if l.N < 1 {
                  size = 1
              } else {
                  size = int(l.N)
              }
          }
          buf = make([]byte, size)
      }
      for {
          nr, er := src.Read(buf)
          if nr > 0 {
              nw, ew := dst.Write(buf[0:nr])
              if nw < 0 || nr < nw {
                  nw = 0
                  if ew == nil {
                      ew = errInvalidWrite
                  }
              }
              written += int64(nw)
              if ew != nil {
                  err = ew
                  break
              }
              // 如果写入的数据无法全部写,则返回 ErrShortWrite 错误
              if nr != nw {
                  err = ErrShortWrite
                  break
              }
          }
          if er != nil {
              if er != EOF {
                  err = er
              }
              break
          }
      }
      // 返回 Copy 的数据长度
      return written, err
    }

MStone
334 声望33 粉丝

全栈攻城狮,PHP++GO+前端,长期工作于创业公司!