go 源码1.10.3 bufio.go
一、bufio介绍
bufio实现了带缓冲的IO功能,它是在io.Reader和io.Writer接口对象上提供了进一步的封装,从而提供了更加丰富的操作方法。bufio包主要
二、bufio.Reader
1.结构体
type Reader struct {
buf []byte //缓冲区的数据
rd io.Reader // 底层的io.Reader
r, w int // r ,w分别表示 buf中读和写的指针位置
err error //记录本次读取的error,后续操作中调用readErr函数后会重置err
lastByte int //记录读取的最后一个字节(用于撤销)
lastRuneSize int //记录读取的最后一个字符(Rune)的长度(用于撤销)
}
bufio.Reader封装了io.Reader对象,并提供了带缓冲的功能。
2. 初始化方法
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
}
if size < minReadBufferSize {
size = minReadBufferSize
}
r := new(Reader)
r.reset(make([]byte, size), rd)
return r
}
size用于指定缓冲区的大小,如果size小于minReadBufferSize,则重置size的值为minReadBufferSize(16)。如果该rd是*bufio.Reader对象,并且rd的缓冲区大于size,则不会创建Reader对象,而是直接返回原来的rd对象。否则会创建一个*bufio.Reader对象,并指定buf的大小为size。
const (
defaultBufSize = 4096
)
func NewReader(rd io.Reader) *Reader {
return NewReaderSize(rd, defaultBufSize)
}
创建缓冲区默认大小为defaultBufSize(4096)的*bufio.Reader对象
3.Reader常用方法
func (b *Reader) Size() int { return len(r.buf) }
Size方法返回底层缓冲区的大小
func (b *Reader) Reset(r io.Reader) {
b.reset(b.buf, r)
}
func (b *Reader) reset(buf []byte, r io.Reader) {
*b = Reader{
buf: buf,
rd: r,
lastByte: -1,
lastRuneSize: -1,
}
}
Reset方法丢弃所有缓冲数据,重置所有状态,并切换reader到r,从而从r中读取数据。
func (b *Reader) fill() {
// Slide existing data to beginning.
if b.r > 0 {
/*
将buf中未读的数据copy到buf中首部位置
重置r和w 位置
*/
copy(b.buf, b.buf[b.r:b.w])
b.w -= b.r
b.r = 0
}
if b.w >= len(b.buf) {
panic("bufio: tried to fill full buffer")
}
// Read new data: try a limited number of times.
/*
maxConsecutiveEmptyReads是最多尝试次数
从rd Reader中读取数据到缓冲区buf中,并重置w的位置索引
*/
for i := maxConsecutiveEmptyReads; i > 0; i-- {
n, err := b.rd.Read(b.buf[b.w:])
if n < 0 {
panic(errNegativeRead)
}
b.w += n
if err != nil {
b.err = err
return
}
if n > 0 {
return
}
}
b.err = io.ErrNoProgress
}
fill方法用于将缓冲区读满,可以读入的最大长度是:len(buf)-未读的字节数,如果尝试读取了maxConsecutiveEmptyReads(100)次都没有读取到数据,则会返回。
func (b *Reader) Peek(n int) ([]byte, error) {
if n < 0 {
return nil, ErrNegativeCount
}
/*
如果buf中未读的字节数小于n
并且buf中未读的字节数小于buf的总大小
并且err为nil
满足以上3个条件,则调用fill方法尝试从Reader中读取部分数据块
*/
for b.w-b.r < n && b.w-b.r < len(b.buf) && b.err == nil {
b.fill() // b.w-b.r < len(b.buf) => buffer is not full
}
/*
如果n大于buf的长度,则返回所有未读的内容,和ErrBufferFull的错误信息
*/
if n > len(b.buf) {
return b.buf[b.r:b.w], ErrBufferFull
}
// 0 <= n <= len(b.buf)
var err error
/*
如果n大于可读的长度,则返回error信息
*/
if avail := b.w - b.r; avail < n {
// not enough data in buffer
n = avail
err = b.readErr()
if err == nil {
err = ErrBufferFull
}
}
return b.buf[b.r : b.r+n], err
}
Peek返回输入流的前n个字节,而不会移动读取位置。该操作不会将数据读出,只是引用,引用的数据在下一次读取操作之前是有效的,如果Peek返回的切片长度比n小,它也会返会一个错误说明原因。如果n比缓冲尺寸还大,返回的错误将是ErrBufferFull。
func (b *Reader) Discard(n int) (discarded int, err error) {
if n < 0 {
return 0, ErrNegativeCount
}
if n == 0 {
return
}
remain := n //remain记录剩余要跳过的字节长度
for {
skip := b.Buffered() //获取buf中可读的数据长度
if skip == 0 {//如果可读的数据长度为0,则尝试从底层rd Reader中读取数据到buf中,然后再获取buf中可读的数据长度
b.fill()
skip = b.Buffered()
}
if skip > remain {//设置skip
skip = remain
}
b.r += skip //设置r的位置,改变读的索引值r
remain -= skip
if remain == 0 { //如果已经跳过n个字节,则返回
return n, nil
}
if b.err != nil {
return n - remain, b.readErr()
}
}
}
Discard 方法跳过后续的 n 个字节的数据,返回跳过的字节数。如果结果小于 n,将返回错误信息。如果 n 小于缓存中的数据长度,则不会从底层提取数据。
func (b *Reader) Read(p []byte) (n int, err error) {
n = len(p)
if n == 0 { //如果p的长度为0,则直接返回
return 0, b.readErr()
}
if b.r == b.w { //缓冲区没有可读的数据
if b.err != nil { //如果上次读取有error,则直接返回
return 0, b.readErr()
}
if len(p) >= len(b.buf) { //如果 len(p) >= 缓冲区大小
// Large read, empty buffer.
// Read directly into p to avoid copy.
n, b.err = b.rd.Read(p) //直接读取rd中的数据到p中
if n < 0 {
panic(errNegativeRead)
}
if n > 0 { //有读取的数据
b.lastByte = int(p[n-1])
b.lastRuneSize = -1
}
return n, b.readErr()
}
// One read.
// Do not use b.fill, which will loop.
/*
如果len(p) < 缓冲区大小,将读(r)的位置和写(w)的位置设为0,并从rd中读取数据到缓冲区buf中
*/
b.r = 0
b.w = 0
n, b.err = b.rd.Read(b.buf)
if n < 0 {
panic(errNegativeRead)
}
if n == 0 {
return 0, b.readErr()
}
b.w += n //更新写的位置
}
// copy as much as we can
n = copy(p, b.buf[b.r:b.w]) //将数据copy到p中
b.r += n //并更新读的位置
b.lastByte = int(b.buf[b.r-1]) //记录读取的最后一个字节
b.lastRuneSize = -1
return n, nil
}
Read从Reader对象b中读出数据到p中,n是返回读取的字节数。如果Reader对象b中的缓冲区buf不为空,则只能读取缓冲中的数据,不会从底层的io.Reader中读取数据。
如果b中的缓冲buf为空,则:
1. len(p) >= 缓冲大小,则跳过缓存,直接从底层io.Reader中读出到p总
2.len(p)<缓存大小,则先将数据从底层的io.Reader中读取到缓存中,再从缓存buf读取到p中。
func (b *Reader) ReadByte() (byte, error) {
b.lastRuneSize = -1
for b.r == b.w {
if b.err != nil {
return 0, b.readErr()
}
b.fill() // buffer is empty
}
c := b.buf[b.r]
b.r++
b.lastByte = int(c) //记录读取的最后一个字节
return c, nil
}
ReadByte 方法读取并返回一个字节的数据,如果没有可读的字节,则返回一个error
// UnreadByte unreads the last byte. Only the most recently read byte can be unread.
func (b *Reader) UnreadByte() error {
if b.lastByte < 0 || b.r == 0 && b.w > 0 { //条件判断
return ErrInvalidUnreadByte
}
// b.r > 0 || b.w == 0
if b.r > 0 { //b.r大于0,则减小读的索引
b.r--
} else { //如果b.r为0,则将写w的索引设为1
// b.r == 0 && b.w == 0
b.w = 1
}
b.buf[b.r] = byte(b.lastByte) //缓冲区buf中添加最后读的一个字节
b.lastByte = -1 //将lastByte置为-1
b.lastRuneSize = -1
return nil
}
UnreadByte方法撤销最近一次读取操作读取 最后一个字节(只能撤销最后一个字节,多次调用会有问题)。
func (b *Reader) ReadRune() (r rune, size int, err error) {
/*
当满足:
1.可读的字节数小于UTFMax(UTFMax是一个UTF字符占用的最大字节数)
2.可读的数据不够填充一个UTF-8字符
3.没有error
4.可读的字节数小于len(b.buf)
当满足以上几个条件会调用fill来从io.Reader中读出内容到buf中
*/
for b.r+utf8.UTFMax > b.w && !utf8.FullRune(b.buf[b.r:b.w]) && b.err == nil && b.w-b.r < len(b.buf) {
b.fill() // b.w-b.r < len(buf) => buffer is not full
}
b.lastRuneSize = -1 //重置lastRuneSize
if b.r == b.w { //当没有可读数据
return 0, 0, b.readErr()
}
r, size = rune(b.buf[b.r]), 1
if r >= utf8.RuneSelf { //不是ascii码
r, size = utf8.DecodeRune(b.buf[b.r:b.w]) //读取一个字符r和长度size
}
b.r += size
b.lastByte = int(b.buf[b.r-1]) //设置lastByte
b.lastRuneSize = size //设置lastRuneSize
return r, size, nil
}
ReadRune 读取一个UTF-8字符,并且返回该字符r,该字符的所占的字节数size,以及可能出现的error,如果UTF-8编码的字符无效,则消耗一个字节并返回大小为1的unicode.ReplacementChar(U + FFFD)
func (b *Reader) UnreadRune() error {
if b.lastRuneSize < 0 || b.r < b.lastRuneSize {
return ErrInvalidUnreadRune
}
b.r -= b.lastRuneSize //修改读的索引r
b.lastByte = -1
b.lastRuneSize = -1
return nil
}
UnreadRune方法撤销上一次读取UTF-8字符的操作
func (b *Reader) Buffered() int { return b.w - b.r }
Buffered方法返回buf中可读的数据长度
func (b *Reader) ReadSlice(delim byte) (line []byte, err error) {
for {
// Search buffer.
//从buf中未读的数据中查找delim字节
//如果可以找到则返回buf中的切片,并设置读的索引位置r,并跳出循环
if i := bytes.IndexByte(b.buf[b.r:b.w], delim); i >= 0 {
line = b.buf[b.r : b.r+i+1]
b.r += i + 1
break
}
// Pending error?
if b.err != nil { //如果上次读操作出现error,则返回
line = b.buf[b.r:b.w]
b.r = b.w
err = b.readErr()
break
}
// Buffer full?
if b.Buffered() >= len(b.buf) { //如果buf是满的,则返回整个buf,并设置错误ErrBufferFull
b.r = b.w
line = b.buf
err = ErrBufferFull
break
}
b.fill() // buffer is not full 如果buf没有满,则向buf中读入数据
}
// Handle last byte, if any.
if i := len(line) - 1; i >= 0 {
b.lastByte = int(line[i]) //设置最后一个字节
b.lastRuneSize = -1
}
return
}
ReadSlice方法在b中查找delim字节并返回delim及之前的所有数据,返回的是buf的切片
如果找到delim,则返回查找结果,err为nil
如果未找到delim,则:
1.缓存不满,则将缓存填满后再次查找
2.缓存是满的,则返回整个缓存,err返回ErrBufferFull
3.如果未找到delim且遇到错误(通常是io.EOF),则返回缓存中的所有数据和遇到的错误。
因为返回的数据有可能被下一次的读写操作修改,所以大多数操作应该使用ReadBytes或ReadString,它们返回的数据的拷贝
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) {
//通过ReadSlice读取一行内容,返回的是buf的切片,在下次的读写操作时内容可能会改变
line, err = b.ReadSlice('\n')
if err == ErrBufferFull {
// Handle the case where "\r\n" straddles the buffer.
if len(line) > 0 && line[len(line)-1] == '\r' {
// Put the '\r' back on buf and drop it from line.
// Let the next call to ReadLine check for "\r\n".
if b.r == 0 {
// should be unreachable
panic("bufio: tried to rewind past start of buffer")
}
b.r--
line = line[:len(line)-1]
}
return line, true, nil
}
if len(line) == 0 {
if err != nil {
line = nil
}
return
}
err = nil
if line[len(line)-1] == '\n' {
drop := 1
if len(line) > 1 && line[len(line)-2] == '\r' {
drop = 2
}
line = line[:len(line)-drop]
}
return
}
ReadLine方法是一个低水平的行读取操作,大多数情况下,应该使用
ReadBytes('\n')或ReadString('\n'),或者使用Scanner
ReadLine 通过调用 ReadSlice 方法实现,返回的也是缓存的切片。用于读取一行数据,不包括行尾标记(\n 或 \r\n)。
func (b *Reader) ReadBytes(delim byte) ([]byte, error) {
// Use ReadSlice to look for array,
// accumulating full buffers.
var frag []byte
var full [][]byte //可能多次从底层io.Reader中读取数据
var err error
for {//可能多次调用ReadSlice去查找delim字节,直到找到或unexpected error(包括io.EOF)
var e error
frag, e = b.ReadSlice(delim)
if e == nil { // got final fragment
break
}
if e != ErrBufferFull { // unexpected error
err = e
break
}
// Make a copy of the buffer.
buf := make([]byte, len(frag))
copy(buf, frag)
full = append(full, buf)
}
// Allocate new buffer to hold the full pieces and the fragment.
n := 0
for i := range full {
n += len(full[i])
}
n += len(frag)
// Copy full pieces and fragment in.
buf := make([]byte, n)
n = 0
for i := range full {
n += copy(buf[n:], full[i])
}
copy(buf[n:], frag)
return buf, err
}
ReadBytes 方法从b中读取数据 直到第一次出现delim字节
功能类似ReadSlice,
不同点:
1.只不过返回的是缓存buf的copy,这样当下次读写操作也不会影响返回的内容
2.ReadSlice最多在整个len(buf)中查找,如果未找到返回ErrBufferFull。但是ReadBytes还会继续从io.Reader中读取数据,直到找到该delim字节,或者io.EOF,或者出现unexpected error
也就是ReadSlice最多返回的切片长度为len(buf),而ReadBytes返回的切片长度可能会大于len(buf)
func (b *Reader) ReadString(delim byte) (string, error) {
bytes, err := b.ReadBytes(delim)
return string(bytes), err
}
ReadString方法功能同ReadBytes,只不过把ReadBytes返回的切片转成字符串
func (b *Reader) WriteTo(w io.Writer) (n int64, err error) {
n, err = b.writeBuf(w)
if err != nil {
return
}
if r, ok := b.rd.(io.WriterTo); ok {
m, err := r.WriteTo(w)
n += m
return n, err
}
if w, ok := w.(io.ReaderFrom); ok {
m, err := w.ReadFrom(b.rd)
n += m
return n, err
}
if b.w-b.r < len(b.buf) {
b.fill() // buffer not full
}
for b.r < b.w {
// b.r < b.w => buffer is not empty
m, err := b.writeBuf(w)
n += m
if err != nil {
return n, err
}
b.fill() // buffer is empty
}
if b.err == io.EOF {
b.err = nil
}
return n, b.readErr()
}
WriteTo 实现了 io.WriterTo,可能会多次调用底层Reader的Read方法。
功能:向w中写入数据, 直到底层的io.Reader(即rd)没有可读的数据
// writeBuf writes the Reader's buffer to the writer.
func (b *Reader) writeBuf(w io.Writer) (int64, error) {
n, err := w.Write(b.buf[b.r:b.w])
if n < 0 {
panic(errNegativeWrite)
}
b.r += n
return int64(n), err
}
writeBuf方法用于将Reader中buffer中可读的数据写入到该writer中
三、bufio.Writer
1.结构体
type Writer struct {
err error //
buf []byte //缓冲区
n int //下次写入缓冲区的索引
wr io.Writer //底层的io.Writer
}
bufio.Writer结构退封装了io.Writer,实现带缓冲的功能。
2.初始化
func NewWriterSize(w io.Writer, size int) *Writer {
// Is it already a Writer?
b, ok := w.(*Writer)
if ok && len(b.buf) >= size {
return b
}
if size <= 0 {
size = defaultBufSize
}
return &Writer{
buf: make([]byte, size),
wr: w,
}
}
返回一个新的bufio.Writer的对象,size为缓冲区buf的至少长度
1.当w为bufio.Writer,并且其buf的大小大于size,则直接返回该对象
2.如果size小于等于0,则defaultBufSize
3.创建新的Writer并返回
func NewWriter(w io.Writer) *Writer {
return NewWriterSize(w, defaultBufSize)
}
创建默认缓冲大小的Writer对象,默认缓冲区为defaultBufSize (4096)
3.Writer常用方法
func (b *Writer) Size() int { return len(b.buf) }
Size方法返回该Writer的buf大小
// Reset discards any unflushed buffered data, clears any error, and
// resets b to write its output to w.
func (b *Writer) Reset(w io.Writer) {
b.err = nil
b.n = 0
b.wr = w
}
Reset方法用于重置Writer中的io.Writer
// Flush writes any buffered data to the underlying io.Writer.
func (b *Writer) Flush() error {
if b.err != nil {
return b.err
}
if b.n == 0 {
return nil
}
n, err := b.wr.Write(b.buf[0:b.n]) //将缓冲区的数据写入到底层io.Writer中
if n < b.n && err == nil {
err = io.ErrShortWrite
}
if err != nil {
if n > 0 && n < b.n {
copy(b.buf[0:b.n-n], b.buf[n:b.n])
}
b.n -= n
b.err = err
return err
}
b.n = 0 //重置n
return nil
}
Flush将缓冲区buf中的数据写入到底层的io.Writer
// Available returns how many bytes are unused in the buffer.
func (b *Writer) Available() int { return len(b.buf) - b.n }
Available方法获取缓冲区buf中可用的字节数
// Buffered returns the number of bytes that have been written into the current buffer.
func (b *Writer) Buffered() int { return b.n }
Buffered方法返回缓冲区buf写入的字节数
func (b *Writer) Write(p []byte) (nn int, err error) {
for len(p) > b.Available() && b.err == nil {//如果len(p)的长度大于缓冲区可用的字节数
var n int
if b.Buffered() == 0 { //buf中没有写入的数据,说明len(p) > buf的长度,则将p直接写入到底层的io.Writer
// Large write, empty buffer.
// Write directly from p to avoid copy.
n, b.err = b.wr.Write(p)
} else {
/*
如果buf中已经有写入的数据,则将p中的内容追加到缓冲区的尾部。
可能p中的数据会全部写入到缓冲区中,也能能p中的数据未全部写入到缓冲区中
*/
n = copy(b.buf[b.n:], p)
b.n += n //移动缓冲区的写的位置n
b.Flush() //刷新缓冲区
}
nn += n
p = p[n:]
}
if b.err != nil {
return nn, b.err
}
n := copy(b.buf[b.n:], p)
b.n += n
nn += n
return nn, nil
}
Write将p的内容写入到缓冲区中,返回写入的长度
如果nn < len(p),将返回一个error来解释原因
需要保证写入的顺序,如果缓冲区buf中已经有写入的数据,则将p中的数据先写到缓冲区中
// WriteByte writes a single byte.
func (b *Writer) WriteByte(c byte) error {
if b.err != nil {
return b.err
}
if b.Available() <= 0 && b.Flush() != nil { //如果缓冲区中没有可用的字节数,则调用Flush刷新缓存区,如果出现error,则返回该error
return b.err
}
b.buf[b.n] = c //将该字节追加到缓冲区中
b.n++ //更新缓存区写的索引
return nil
}
WriteByte方法用向Writer中写入一个字节
func (b *Writer) WriteRune(r rune) (size int, err error) {
if r < utf8.RuneSelf { //r是一个字节,则调用WriteByte
err = b.WriteByte(byte(r))
if err != nil {
return 0, err
}
return 1, nil
}
if b.err != nil {
return 0, b.err
}
n := b.Available()
if n < utf8.UTFMax { //如果缓冲区中可用的字节小于UTFMax(字符最多占用4个字节)
if b.Flush(); b.err != nil { //刷新缓冲区
return 0, b.err
}
n = b.Available()
if n < utf8.UTFMax {
// Can only happen if buffer is silly small.
return b.WriteString(string(r)) //如果缓冲区的长度小于4,则调用WriteString写入到底层的io.Writer中
}
}
size = utf8.EncodeRune(b.buf[b.n:], r)//将该字符写入到缓冲区中,并返回大小
b.n += size
return size, nil
}
WriteRune方法用于向Writer中写入一个字符
func (b *Writer) WriteString(s string) (int, error) {
nn := 0
for len(s) > b.Available() && b.err == nil { //如果字符串s大于缓冲区中可用的字节数,则先把缓冲区写满,然后调用Flush刷新
n := copy(b.buf[b.n:], s)
b.n += n
nn += n
s = s[n:]
b.Flush()
}
if b.err != nil {
return nn, b.err
}
n := copy(b.buf[b.n:], s) //将s中剩余的写入到缓冲区buf中
b.n += n
nn += n
return nn, nil
}
WriteString方法用于向Writer中写入字符串s
// ReadFrom implements io.ReaderFrom.
func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
if b.Buffered() == 0 { //缓冲区为空
if w, ok := b.wr.(io.ReaderFrom); ok { //如果wr实现了io.ReaderFrom,则调用ReadFrom将r的数据写入到w中
return w.ReadFrom(r)
}
}
var m int
for {
if b.Available() == 0 { //如果缓冲区已满,则调用Flush刷新缓存
if err1 := b.Flush(); err1 != nil {
return n, err1
}
}
nr := 0
for nr < maxConsecutiveEmptyReads {
m, err = r.Read(b.buf[b.n:]) //将r中的数据读出的缓冲区中
if m != 0 || err != nil { //如果有读出的数据,或者err不为nil,则跳出该for循环
break
}
nr++
}
if nr == maxConsecutiveEmptyReads { //如果连续尝试读取了100次,都没有读出数据,则返回error
return n, io.ErrNoProgress
}
b.n += m //改变缓冲区写入的索引
n += int64(m)
if err != nil {
break
}
}
if err == io.EOF {
// If we filled the buffer exactly, flush preemptively.
if b.Available() == 0 {
err = b.Flush()
} else {
err = nil
}
}
return n, err
}
ReadFrom实现了io.ReaderFrom,将r中的数据读出到Writer中
四、bufil.ReadWriter
// buffered input and output
// ReadWriter stores pointers to a Reader and a Writer.
// It implements io.ReadWriter.
type ReadWriter struct {
*Reader
*Writer
}
// NewReadWriter allocates a new ReadWriter that dispatches to r and w.
func NewReadWriter(r *Reader, w *Writer) *ReadWriter {
return &ReadWriter{r, w}
}
ReadWriter封装了bufio.Reader和bufio.Writer