go基础——buffer

酷酷码农 · · 544 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

内容

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

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

544 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传