美文网首页
go基础——buffer

go基础——buffer

作者: chase_lwf | 来源:发表于2020-10-05 19:33 被阅读0次

    内容

    1 bytes.Buffer
    2 strings.Builder
    3 bufio包

    前言
    bytes.Buffer strings.Builder和bufio都是golang中经常使用的提供了缓冲池的数据结构,他们内部都是封装了一个[]byte, 可以用来高效的操作字节切片、字符串和io操作

    一 byte.Buffer

    byte.Buffer是一个简单字节缓冲池,内部包了一个字节数组,在某些频繁io的操作中,可以使用buffer来做一个读取或是写入的缓冲池,来提高效率

    // A Buffer is a variable-sized buffer of bytes with Read and Write methods.
    // The zero value for Buffer is an empty buffer ready to use.
    type Buffer struct {
        buf      []byte // contents are the bytes buf[off : len(buf)]
        off      int    // read at &buf[off], write at &buf[len(buf)]
        lastRead readOp // last read operation, so that Unread* can work correctly.
    }
    

    二 strings.builder

    strings.builder可以高效的写入、拼接字符串,其内部封装了一个字节数组,写入时其实是将传入的字节append到内部的字节数组上

    // A Builder is used to efficiently build a string using Write methods.
    // It minimizes memory copying. The zero value is ready to use.
    // Do not copy a non-zero Builder.
    type Builder struct {
        addr *Builder // of receiver, to detect copies by value
        buf  []byte
    }
    
    // Write appends the contents of p to b's buffer.
    // Write always returns len(p), nil.
    func (b *Builder) Write(p []byte) (int, error) {
        b.copyCheck()
        b.buf = append(b.buf, p...)
        return len(p), nil
    }
    
    • 当builder已经写入值后,或是严格来说其内部切片已经不为nil后,不允许拷贝builder,因为builder内部实际是使用一个切片,拷贝builder后,它们底层还是共享同一个数组,会发生覆盖操作,例如下面这样是不允许的
        builder := strings.Builder{}
        builder.WriteString("haha")
        builder2 := builder
        builder2.WriteString("ww")
    

    builder2.WriteString会调用copyCheck检查内部切片是否为nil,不为nil直接panic

    func (b *Builder) copyCheck() {
        if b.addr == nil {
            // This hack works around a failing of Go's escape analysis
            // that was causing b to escape and be heap allocated.
            // See issue 23382.
            // TODO: once issue 7921 is fixed, this should be reverted to
            // just "b.addr = b".
            b.addr = (*Builder)(noescape(unsafe.Pointer(b)))
        } else if b.addr != b {
            panic("strings: illegal use of non-zero Builder copied by value")
        }
    }
    

    三 bufio

    bufio包提供了有缓冲的io,它定义了两个结构体,分别是Reader和Writer, 它们也分别实现了io包中io.Reader和io.Writer接口, 通过传入一个io.Reader的实现对象和一个缓冲池大小参数,可以构造一个bufio.Reader对象,根据bufio.Reader的相关方法便可读取io.Reader中数据流,因为带有缓冲池,读数据会先读到缓冲池,再次读取会先去缓冲池读取,这样减少了io操作,提高了效率;

    // 构造Reader对象
    func NewReaderSize(rd io.Reader, size int) *Reader {}
    
    // Reader implements buffering for an io.Reader object.
    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 // last byte read for UnreadByte; -1 means invalid
        lastRuneSize int // size of last rune read for UnreadRune; -1 means invalid
    }
    
    // Writer implements buffering for an io.Writer object.
    type Writer struct {
        err error
        buf []byte
        n   int
        wr  io.Writer
    }
    

    func (b *Reader) Read(p []byte) (n int, err error) 具体读取流程如下:

    • 当缓存区有内容的时,将缓存区内容读取到p并清空缓存区;
    • 当缓存区没有内容的时候且len(p)>len(buf),即要读取的内容比缓存区还要大,直接读取文件;
    • 当缓存区没有内容的时候且len(p)<len(buf),即要读取的内容比缓存区小,缓存区从文件读取内容充满缓存区,并将p填满(此时缓存区有剩余内容)
    • 以后再次读取时缓存区有内容,将缓存区内容全部填入p并清空缓存区;

    func (b *Writer) Write(p []byte) (nn int, err error) 具体写入流程如下:

    • 判断buf中可用容量是否可以放下 p;如果能放下,直接把p拼接到buf后面,即把内容放到缓冲区
    • 如果缓冲区的可用容量不足以放下,且此时缓冲区是空的,直接把p写入文件即可
    • 如果缓冲区的可用容量不足以放下,且此时缓冲区有内容,则用p把缓冲区填满,把缓冲区所有内容写入文件,并清空缓冲区;

    引用:

    相关文章

      网友评论

          本文标题:go基础——buffer

          本文链接:https://www.haomeiwen.com/subject/bhgvjktx.html