Golang bufio包源码分析

golang中bufio包的实现原理 最近用golang写了一个处理文件的脚本,由于其中涉及到了文件读写,开始使用golang中的 io 包,后来发现golang 中提供了一个bufio的包,使用这个包可以大幅提高文件读写的效率,于是在网上搜索同样的文件读写为什么bufio 要比io 的读写更快速呢?根据网上的资料和阅读源码,以下来详细解释下bufio的高效如何实现的。 bufio 包介绍 bufio包实现了有缓冲的I/ 阅读详情

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

源码分析之Bufio 源码分析之Bufio 源码路径:/usr/local/go/src/bufio Bufio 有缓冲区的IO,在数据批量操作上相对原生IO效率具有提高; Bufio含有三个对象【type Reader\type Writer\type ReadWriter】;三个常量【defaultBufSize\minReadBufferSize\maxConsecutiveEmptyReads】;四个全局错误变量【ErrInvalidUnreadByte\ErrInvalidUnreadRune\ErrBufferFu 阅读详情

相关推荐

Python新手必看:5分钟搞定睿尔曼机械臂基础控制(附代码示例)

本文为Python新手提供了快速上手睿尔曼机械臂基础控制的实战指南。通过详细的步骤讲解和代码示例,介绍了从环境搭建、网络配置、SDK安装到建立连接、执行关节运动(movej)、直线运动(movel)等核心控制指令的全过程,帮助零基础开发者快速实现机械臂的基础操控。

hp777的博客 330

Go语言源码包以及常用源码包介绍

下面罗列了所有的Go语言源码包,以及各个包的职责,并且加粗我们日常开发使用最频繁的包,需要我们重点研究源码并理解。

yuxiangjin的博客 1249

SNN目标识别MNIST

总体来说这篇论文提出的方法用了多种更精密的细节,如LIF膜电位公式,三种STDP方法,但我觉得还可以有很大的改进,比如输入层全连接到兴奋层,兴奋层通过抑制层实现的侧向抑制,抑制层不参与学习等等。尤其是兴奋层通过抑制层实现侧向抑制这里,当神经元兴奋时,通过抑制层再倒回兴奋层,所抑制的已经经过一了一段时间的延迟。如有错误,请不吝赐教。

m0_74982304的博客 1263

Go源码学习:bufio包 - 1.1 - bufio.go -(1)

Go:bufio:bufio包的作用、目的和基本结构、Size、Reset、fill、Peek、Discard

风不归的乐园 1484

电子纸价签实战:基于SSD1680的墨水屏驱动开发与优化

本文详细介绍了基于SSD1680驱动芯片的电子纸价签开发实战,涵盖硬件连接、驱动框架搭建、核心流程解析及性能优化。重点剖析了初始化配置、波形文件加载、图像数据写入与刷新触发等关键步骤,并提供了功耗优化、显示效果提升及代码健壮性等实用避坑指南,帮助开发者高效实现稳定低功耗的墨水屏驱动。

weixin_28278377的博客 231

有限元缺陷:剪切锁死

有限元缺陷:剪切锁死

wsxww11的博客 5191

Golang学习 - bufio 包

------------------------------------------------------------ // bufio 包实现了带缓存的 I/O 操作 ------------------------------------------------------------ type Reader struct { ... } // NewReaderSi...

weixin_30900589的博客 266

IB网络下不同租户业务隔离方案

IB(InfiniBand)网络隔离可以在不同的通信模式下实现,其中包括 IPoIB(IP over InfiniBand)通信模式。在 IPoIB 模式下,InfiniBand 网络可以承载 IP 数据包,使得基于 IP 的应用程序能够在 InfiniBand 网络上进行通信。在这种情况下,IB 网络隔离通常指通过配置 Partition Key(PKey)来实现不同的租户或应用程序之间的网络隔离。然而,IB 网络隔离并不仅限于 IPoIB 模式。

guganly的专栏 1529

go 输入输出流(bufio)

bufio 包实现了缓存IO。它包装了 io.Reader 和 io.Writer 对象,创建了另外的Reader和Writer对象,它们也实现了io.Reader和io.Writer接口,不过它们是有缓存的。该包同时为文本I/O提供了一些便利操作。1.1. 1.4.1 Reader 类型和方法bufio.Reader 结构包装了一个 io.Reader 对象,提供缓存功能,同时实现了 io.Re...

tianlongtc的博客 4089

【ROS】Nav2源码之nav2_collision_monitor(碰撞监测)详解

nav2_collision_monitor碰撞监视器是一个节点,为机器人提供了一个额外的安全级别。它使用来自传感器的数据,执行若干与避免碰撞相关的任务,绕过成本图和轨迹规划器,监测是否需要紧急停止或者减速。

郭老二 1458

增益可控放大电路-电路与电子技术课程设计

增益可控放大电路-电路与电子技术课程设计

Yeats_Liao的博客 1万+

Go源码学习:bufio包 - 1.1 - bufio.go -(3)

Go:bufio:WriteTo、writeBuf、buffered output、Size、Reset、Flush、Available、AvailableBuffer、Buffered、Write

风不归的乐园 1173

《FreeRTOS 入门与实战》第20章:移植到 RISC-V 平台

处理器上下文保存/恢复机制中断处理与任务切换的协同时钟源配置(通常使用 Machine Timer)平台特定的汇编代码实现// 典型RISC-V移植文件结构FreeRTOS/├── Source/portable/[编译器]/RISC-V/│ ├── port.c // 关键移植接口│ ├── portASM.s // 汇编级处理│ └── portmacro.h // 架构宏定义。

2301_79143213的博客 450

Go 语言从入门到进阶 | 第 10 章:I/O 与文件操作

Go 语言从入门到进阶:耿雨飞。

weixin_43674738的博客 204

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

GO 标准库 学习记录

Go标准库线上学习网站(带例子) https://books.studygolang.com/The-Golang-Standard-Library-by-Example/ bufio包 Go语言在io操作中,还提供了一个bufio的包,使用这个包可以大幅提高文件读写的效率。 bufio 包介绍 bufio包实现了有缓冲的I/O。它包装一个io.Reader或io.Writer接口对象,创建另一个也实现了该接口,且同时还提供了缓冲和一些文本I/O的帮助函数的对象。 以上为官方包的介...

qq_40601603的博客 306

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

golang源码分析-bufio

前言 ​ 流工具包,提供了读取器和写入器的对象创建和一些工具方法。 包介绍 // Package bufio implements buffered I/O. It wraps an io.Reader or io.Writer // object, creating another object (Reader or Writer) that also implements // the interface but provides buffering and some help for textua

tiehui1987的博客 241

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Golang源码分析-bufio.go

bufio.go 是 Go 标准库 bufio 包的核心文件,它主要实现了带缓冲的 I/O 操作,如 Reader 和 Writer。这在性能优化中非常关键,能显著减少系统调用次数,提高 I/O 效率。

gopher123的博客 419

golang的bufio源码分析

原本只想用用bufio,但是网上文章没有一个写清楚bufio到底怎么用,每个方法具体干了什么,搞不明白原理就不敢乱用,还好有源码,自己来分析最清楚。Reader分析func NewReaderSize(rd io.Reader, size int) *Reader { // Is it already a Reader? b, ok := rd.(*Reader) if ok

云上听风的博客 605

bufio详解

bufio包实现了有缓冲的I/O,它封装了一个io.Reader或io.Write接口对象,创建一个实现实现了该接口,同时提供缓冲和一些文本I/O的帮助函数. type Reader struct { buf []byte rd io.Reader // reader provided by the client r, w int // buf read and write positions

manclient的博客 4212

Golang bufio Reader 源码详解

是对 gosdk 种 和 的二次打包,以 作为其 buffer,在 和 的基础上提供了更多方便有效的 io 读取和写入方法。PS:由于代码过多,对源码函数流程的解释直接编写在源码注释上 和 是 gosdk 包中的非常重要的两个接口,它们内部各包含一个方法 和 ,传入参数 ,返回读取/写入的字节数和错误。 核心作用为连续读取字节填充传入的 slice,并在读取停止时返回字节数和遇到的错误(无错误时返回nil)。 该结构体是 种所有读取相关方法的 receiver,即所有读取操作都需要在创建

ElzatAhmed的博客 1299

Golang源码分析

Golang源码分析 Golang源码分析-builtin.go Golang源码分析-bytes.go Golang源码分析-bytes/buffer.go Golang源码分析-bytes/reader.go Golang源码分析-strings.go Golang源码分析-strings builder.go Golang源码分析-strconv包 Golang源码分析-io.go Golang源码分析-io/pipe.go Golang源码分析-io/ioutil.go Gol

hixiaoyang的博客 793

Golang源码分析-io.go

在 Go 源码中,io.go 位于标准库 io 包下(src/io/io.go)。 io 包是 输入输出的核心抽象层,它定义了统一的接口,屏蔽了具体数据源(文件、网络、内存缓冲区等)的差异。 也就是说,Go 里无论是操作文件、网络、内存,底层用的都是 io 包里定义的接口,确保代码 可组合、可替换、可扩展。

gopher123的博客 1037

Golang bufio.Reader ReadLine学习

之前学习go的时候被这个ReadLine坑了一段时间,以为加个for循环就会一行一行的读下去。当时好像调试了半天之类的,总之今天来带你们看看他怎么实现的吧。 首先我们构造一个Reader方便我们调试与分析: sr := strings.NewReader(&quot;123\n456&quot;); reader := bufio.NewReader(sr); line, _, err := reader...

a99361481的博客 1万+

标准库bufio个人详解

本文是我有通俗的语言写的如果有误请指出。 先看bufio官方文档 https://studygolang.com/pkgdoc文档地址 主要分三部分Reader、Writer、Scanner 分别是读数据、写数据和扫描器三种数据类型的相关操作 这个扫描后面会详细说我开始也没弄明白其实很简单。 Reader funcNewReaderSize比奇小说网 https://m.b...

爱无止 525
上一篇: kubeadm获取或创建加入k8s集群的token
下一篇: Golang bufio包Scanner源码
思维的深度
博客等级 码龄11年 2万+粉丝 391原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值