bufio

Reader

bufio.Readerio.Reader进行了包装,提供了缓冲区功能。定义如下:

type Reader struct {
    buf          []byte
    rd           io.Reader // reader provided by the client
    r, w         int       // buf read and write positions
    err          error
    lastByte     int       // 最后一个读取的字节,用于UnreadByte操作
    lastRuneSize int       // 最后一个读取rune的大小,用于UnreadRune操作
}

创建

通过NewReader(rd io.Reader)可以创建一个新的Reader:

func NewReader(rd io.Reader) *Reader {
    // const defaultBufSize = 4096
    // 默认缓冲区大小为4K
    return NewReaderSize(rd, defaultBufSize)
}

可以看到,NewReader实际上是调用了NewReaderSize方法,NewReaderSize会创建一个具有特定大小缓冲区的Reader:

func NewReaderSize(rd io.Reader, size int) *Reader {
    // Is it already a Reader?
    b, ok := rd.(*Reader)
    if ok && len(b.buf) >= size {
        return b
    }
    // const minReadBufferSize = 16
    // 缓冲区最小为16byte
    if size < minReadBufferSize {
        size = minReadBufferSize
    }
    r := new(Reader)
    r.reset(make([]byte, size), rd)
    return r
}

func (b *Reader) reset(buf []byte, r io.Reader) {
    *b = Reader{
        buf:          buf,
        rd:           r,
        lastByte:     -1,
        lastRuneSize: -1,
    }
}

read

和读操作相关的方法有:

  • func (b *Reader) Read(p []byte) (n int, err error)
  • func (b *Reader) ReadByte() (byte, error)
  • func (b *Reader) ReadRune() (r rune, size int, err error)
  • func (b *Reader) UnreadByte() error
  • func (b *Reader) UnreadRune() error
  • func (b *Reader) ReadSlice(delim byte) (line []byte, err error)
  • func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)
  • func (b *Reader) ReadBytes(delim byte) ([]byte, error)
  • func (b *Reader) ReadString(delim byte) (string, error)

其中Read方法源码为:

func (b *Reader) Read(p []byte) (n int, err error) {
    n = len(p)
    if n == 0 {
        return 0, b.readErr()
    }
    // 如果b.r == b.w,则当前缓冲区中无数据
    if b.r == b.w {
        if b.err != nil {
            return 0, b.readErr()
        }
        // 如果p的大小大于等于缓冲区大小,则直接将数据读入p,然后返回
        if len(p) >= len(b.buf) {
            // Large read, empty buffer.
            // Read directly into p to avoid copy.
            n, b.err = b.rd.Read(p)
            if n < 0 {
                panic(errNegativeRead)
            }
            if n > 0 {
                b.lastByte = int(p[n-1])
                b.lastRuneSize = -1
            }
            return n, b.readErr()
        }
        // 如果p的大小小于缓冲区大小,则先将数据读入缓冲区
        b.fill() // buffer is empty
        if b.r == b.w {
            return 0, b.readErr()
        }
    }

    // 将缓冲区中的数据尽可能的拷贝到p中
    n = copy(p, b.buf[b.r:b.w])
    b.r += n
    b.lastByte = int(b.buf[b.r-1])
    b.lastRuneSize = -1
    return n, nil
}

ReadSlice(delim byte)会读取数据直到遇到分隔符delim。如果在遇到delim之前出错了或者缓冲区满了,也会退出。

ReadLine()会读取一行数据,同样,在遇到换行符之前,如果出错了或者缓冲区满了,也会退出。因此该方法并不能保证遇到换行符的时候返回,也就是说,读到的数据可能并不够一行。例如:

r := strings.NewReader("0123456789abcdefghijklmn\nopqrstuvwxyz")
br := bufio.NewReaderSize(r, 16)
line, isPrefix, err := br.ReadLine()
fmt.Println(string(line)) // 0123456789abcdef
fmt.Println(isPrefix)     // true
fmt.Println(err)          // <nil>  

因此,如果想要按行读取数据,使用ReadBytes('\n')或者ReadString('\n')会是更好的选择。ReadString实际上调用了ReadBytes,只不过将数据转成了字符串而已。ReadBytes(delim byte)会不断地读取数据,直到遇到分隔符delim。例如:

r := strings.NewReader("0123456789abcdefghijklmn\nopqrstuvwxyz")
br := bufio.NewReaderSize(r, 16)
line, err := br.ReadBytes('\n')
fmt.Println(string(line)) // 0123456789abcdefghijklmn
fmt.Println(err)          // <nil>

其它操作

  • func (b *Reader) Buffered() int
  • func (b *Reader) Reset(r io.Reader)
  • func (b *Reader) Discard(n int) (discarded int, err error)
  • func (b *Reader) Peek(n int) ([]byte, error)
  • func (b *Reader) WriteTo(w io.Writer) (n int64, err error)

Buffered返回当前缓冲区中的可用数据量:

func (b *Reader) Buffered() int { return b.w - b.r }

Reset会重置数据源,之后的数据读取都会从新的数据源中来读:

func (b *Reader) Reset(r io.Reader) {
    b.reset(b.buf, r)
}

Discard(n int)会跳过之后的n个字节,例如:

br := bufio.NewReader(strings.NewReader("0123456789"))
p := make([]byte, 5)
br.Discard(3)
br.Read(p)
fmt.Println(string(p)) // 34567

Peek(n int)用于查看接下来的n个字节数据,但是并不真正读取,例如:

br := bufio.NewReader(strings.NewReader("0123456789"))
p := make([]byte, 5)
br.Peek(3)
br.Read(p)
fmt.Println(string(p)) // 01234

WriteTo将数据写入到一个Writer中,因此bufio.Reader实现了io.WriterTo接口。

Writer

bufio.Writerio.Writer进行了包装,提供了缓冲区功能。定义如下:

type Writer struct {
    err error
    buf []byte // 缓冲区
    n   int    // 缓冲区的可用数据量
    wr  io.Writer
}

通过如下方法可以创建新的Writer:

  • func NewWriter(w io.Writer) *Writer
  • func NewWriterSize(w io.Writer, size int) *Writer

write

写操作相关方法有:

  • func (b *Writer) Write(p []byte) (nn int, err error)
  • func (b *Writer) WriteByte(c byte) error
  • func (b *Writer) WriteRune(r rune) (size int, err error)
  • func (b *Writer) WriteString(s string) (int, error)

其中,Write方法源码为:

func (b *Writer) Write(p []byte) (nn int, err error) {
    // b.Available() 的值为 len(b.buf) - b.n
    // 只要p的大小大于缓冲区的可用大小,则执行循环
    for len(p) > b.Available() && b.err == nil {
        var n int
        if b.Buffered() == 0 {
            // 如果p的大小大于缓冲区的可用大小,且缓冲区为空
            // 则数据直接写入,无需先拷贝到缓冲区
            n, b.err = b.wr.Write(p)
        } else {
            // 将数据拷贝到缓冲区,然后通过flush操作写入缓冲区数据
            n = copy(b.buf[b.n:], p)
            b.n += n
            b.flush()
        }
        nn += n
        // 剩余待写入数据
        p = p[n:]
    }
    if b.err != nil {
        return nn, b.err
    }
    // 此时p的大小小于等于缓冲区大小,因此将数据拷贝到缓冲区
    n := copy(b.buf[b.n:], p)
    b.n += n
    nn += n
    return nn, nil
}

其它操作

  • func (b *Writer) Available() int
  • func (b *Writer) Buffered() int
  • func (b *Writer) Flush() error
  • func (b *Writer) ReadFrom(r io.Reader) (n int64, err error)
  • func (b *Writer) Reset(w io.Writer)

Available()返回的是缓冲区中的可用大小:

func (b *Writer) Available() int { return len(b.buf) - b.n }

Buffered()返回的是缓冲区中已经缓存的数据大小:

func (b *Writer) Buffered() int { return b.n }

ReadWriter

bufio.ReadWriter实现了io.ReadWriter接口,同时包含了一个Reader和一个Writer:

type ReadWriter struct {
    *Reader
    *Writer
}

func NewReadWriter(r *Reader, w *Writer) *ReadWriter {
    return &ReadWriter{r, w}
}