go原生库的中bytes.Buffer用法


Posted in Golang onApril 25, 2021

1 bytes.Buffer定义

bytes.Buffer提供可扩容的字节缓冲区,实质是对切片的封装;结构中包含一个64字节的小切片,避免小内存分配:

// 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)]--->指示读指针
 bootstrap [64]byte // memory to hold first slice; helps small buffers avoid allocation.
 lastRead  readOp   // last read operation, so that Unread* can work correctly.
}

2 初始化bytes.Buffer的方法

1) var buf bytes.Buffer ->定义一个空的字节缓冲区

2) func NewBuffer(buf []byte) *Buffer { return &Buffer{buf: buf} } -->将字节切片初始化为缓冲区

3) func NewBufferString(s string) *Buffer {return &Buffer{buf: []byte(s)}} -->将字符串初始化为缓冲区

3 提供的主要API函数

1)写字节流数据到缓冲区

// Write appends the contents of p to the buffer, growing the buffer as
// needed. The return value n is the length of p; err is always nil. If the
// buffer becomes too large, Write will panic with ErrTooLarge.
func (b *Buffer) Write(p []byte) (n int, err error) {
 b.lastRead = opInvalid
 m := b.grow(len(p))
 return copy(b.buf[m:], p), nil
}

2)写字符串到缓冲区

// WriteString appends the contents of s to the buffer, growing the buffer as
// needed. The return value n is the length of s; err is always nil. If the
// buffer becomes too large, WriteString will panic with ErrTooLarge.
func (b *Buffer) WriteString(s string) (n int, err error) {
 b.lastRead = opInvalid
 //返回写入的index
 m := b.grow(len(s))
 return copy(b.buf[m:], s), nil
}

3)从缓冲区中读取数据

// Read reads the next len(p) bytes from the buffer or until the buffer
// is drained. The return value n is the number of bytes read. If the
// buffer has no data to return, err is io.EOF (unless len(p) is zero);
// otherwise it is nil.
func (b *Buffer) Read(p []byte) (n int, err error) {
 b.lastRead = opInvalid
 if b.off >= len(b.buf) {
  // Buffer is empty, reset to recover space.
  b.Truncate(0)
  if len(p) == 0 {
   return
  }
  return 0, io.EOF
 }
 n = copy(p, b.buf[b.off:])
 b.off += n
 if n > 0 {
  b.lastRead = opRead
 }
 return
}

4)从缓冲区中读取字符串,直到分隔符delim 位置

// ReadString reads until the first occurrence of delim in the input,
// returning a string containing the data up to and including the delimiter.
// If ReadString encounters an error before finding a delimiter,
// it returns the data read before the error and the error itself (often io.EOF).
// ReadString returns err != nil if and only if the returned data does not end
// in delim.
func (b *Buffer) ReadString(delim byte) (line string, err error) {
 slice, err := b.readSlice(delim)
 return string(slice), err
}

5)将未被读取的字节数据返回

// Bytes returns a slice of length b.Len() holding the unread portion of the buffer.
// The slice is valid for use only until the next buffer modification (that is,
// only until the next call to a method like Read, Write, Reset, or Truncate).
// The slice aliases the buffer content at least until the next buffer modification,
// so immediate changes to the slice will affect the result of future reads.
func (b *Buffer) Bytes() []byte { return b.buf[b.off:] }

6)将未被读取的字节数据以字符串形式返回

// String returns the contents of the unread portion of the buffer
// as a string. If the Buffer is a nil pointer, it returns "<nil>".
func (b *Buffer) String() string {
 if b == nil {
  // Special case, useful in debugging.
  return "<nil>"
 }
 return string(b.buf[b.off:])
}

7)返回缓冲区当前容量

// Cap returns the capacity of the buffer's underlying byte slice, that is, the
// total space allocated for the buffer's data.
func (b *Buffer) Cap() int { return cap(b.buf) }

8)返回未被读取的字节数据大小

// Len returns the number of bytes of the unread portion of the buffer;
// b.Len() == len(b.Bytes()).
func (b *Buffer) Len() int { return len(b.buf) - b.off }

4 bytes.Buffer自动扩容机制

当向缓冲区写入数据时,首先会检查当前容量是否满足需求,如果不满足分三种情况处理:

1)当前内置缓冲区切片buf为空,且写入数据量小于bootstrap的大小(64字节),则bootstrap作为buf

2)当前未读数据长度+新写入数据长度小于等于缓冲区容量的1/2,则挪动数据(将未读的数据放到已读数据位置)

3)以上条件不满足,只能重新分配切片,容量设定为2*cap(b.buf) + n,即两倍原来的缓冲区容量+写入数据量大小

// grow grows the buffer to guarantee space for n more bytes.
// It returns the index where bytes should be written.
// If the buffer can't grow it will panic with ErrTooLarge.
func (b *Buffer) grow(n int) int {
 m := b.Len()
 // If buffer is empty, reset to recover space.
 if m == 0 && b.off != 0 {
  b.Truncate(0)
 }
 //如果需要的容量大于现在的容量--->
 if len(b.buf)+n > cap(b.buf) {
  var buf []byte
  //现有的预备64byte可以满足
  if b.buf == nil && n <= len(b.bootstrap) {
   buf = b.bootstrap[0:]
   //实际需要的小于本身切片容量
  } else if m+n <= cap(b.buf)/2 {
   // We can slide things down instead of allocating a new
   // slice. We only need m+n <= cap(b.buf) to slide, but
   // we instead let capacity get twice as large so we
   // don't spend all our time copying.
   copy(b.buf[:], b.buf[b.off:])
   buf = b.buf[:m]
  } else {
   // not enough space anywhere
   //不够,那就分配2倍加n的容量
   buf = makeSlice(2*cap(b.buf) + n)
   copy(buf, b.buf[b.off:])
  }
  b.buf = buf
  b.off = 0
 }
 b.buf = b.buf[0 : b.off+m+n]
 return b.off + m
}

5 bytes.Buffer的局限

bytes.Buffer提供了对切片的初步封装,但也没做太多的事;对于已读的数据无法操作。

补充:Golang bytes.Buffer 用法精述

1.简介

bytes.Buffer 是 Golang 标准库中的缓冲区,具有读写方法和可变大小的字节存储功能。缓冲区的零值是一个待使用的空缓冲区。

定义如下:

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.
}

注意要点:

(1)从 bytes.Buffer 读取数据后,被成功读取的数据仍保留在原缓冲区,只是无法被使用,因为缓冲区的可见数据从偏移 off 开始,即buf[off : len(buf)]。

2.常用方法

(1)声明一个 Buffer

var b bytes.Buffer           //直接定义一个Buffer变量,不用初始化,可以直接使用
b := new(bytes.Buffer)       //使用New返回Buffer变量
b := bytes.NewBuffer(s []byte)     //从一个[]byte切片,构造一个Buffer
b := bytes.NewBufferString(s string) //从一个string变量,构造一个Buffer

(2)往 Buffer 中写入数据

b.Write(d []byte) (n int, err error)      //将切片d写入Buffer尾部
b.WriteString(s string) (n int, err error)   //将字符串s写入Buffer尾部
b.WriteByte(c byte) error        //将字符c写入Buffer尾部
b.WriteRune(r rune) (n int, err error)      //将一个rune类型的数据放到缓冲区的尾部
b.ReadFrom(r io.Reader) (n int64, err error) //从实现了io.Reader接口的可读取对象写入Buffer尾部

(3)从 Buffer 中读取数据

//读取 n 个字节数据并返回,如果 buffer 不足 n 字节,则读取全部
b.Next(n int) []byte
//一次读取 len(p) 个 byte 到 p 中,每次读取新的内容将覆盖p中原来的内容。成功返回实际读取的字节数,off 向后偏移 n,buffer 没有数据返回错误 io.EOF
b.Read(p []byte) (n int, err error)
//读取第一个byte并返回,off 向后偏移 n
b.ReadByte() (byte, error)
//读取第一个 UTF8 编码的字符并返回该字符和该字符的字节数,b的第1个rune被拿掉。如果buffer为空,返回错误 io.EOF,如果不是UTF8编码的字符,则消费一个字节,返回 (U+FFFD,1,nil)
b.ReadRune() (r rune, size int, err error)
//读取缓冲区第一个分隔符前面的内容以及分隔符并返回,缓冲区会清空读取的内容。如果没有发现分隔符,则返回读取的内容并返回错误io.EOF
b.ReadBytes(delimiter byte) (line []byte, err error)
//读取缓冲区第一个分隔符前面的内容以及分隔符并作为字符串返回,缓冲区会清空读取的内容。如果没有发现分隔符,则返回读取的内容并返回错误 io.EOF
b.ReadString(delimiter byte) (line string, err error)
//将 Buffer 中的内容输出到实现了 io.Writer 接口的可写入对象中,成功返回写入的字节数,失败返回错误
b.WriteTo(w io.Writer) (n int64, err error)

(4)其它操作

b.Bytes() []byte  //返回字节切片
b.Cap() int    //返回 buffer 内部字节切片的容量
b.Grow(n int)   //为 buffer 内部字节切片的容量增加 n 字节
b.Len() int    //返回缓冲区数据长度,等于 len(b.Bytes())
b.Reset()     //清空数据
b.String() string  //字符串化
b.Truncate(n int)  //丢弃缓冲区中除前n个未读字节以外的所有字节。如果 n 为负数或大于缓冲区长度,则引发 panic
b.UnreadByte() error //将最后一次读取操作中被成功读取的字节设为未被读取的状态,即将已读取的偏移 off 减 1
b.UnreadRune() error //将最后一次 ReadRune() 读取操作返回的 UTF8 字符 rune设为未被读取的状态,即将已读取的偏移 off 减去 字符 rune 的字节数

3.使用示例

(1)从文件 test.txt 中读取全部内容追加到 buffer 尾部

test.txt 的内容为:

My name is dablelv

具体实现:

package main
import (
 "os"
 "fmt"
 "bytes"
)
func main() {
    file, _ := os.Open("./test.txt")    
    buf := bytes.NewBufferString("Hello world ")    
    buf.ReadFrom(file)              //将text.txt内容追加到缓冲器的尾部    
    fmt.Println(buf.String())
}

编译运行输出:

Hello world My name is dablelv

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。如有错误或未考虑完全的地方,望不吝赐教。

Golang 相关文章推荐
go 原生http web 服务跨域restful api的写法介绍
Apr 27 Golang
golang 实现两个结构体复制字段
Apr 28 Golang
golang import自定义包方式
Apr 29 Golang
使用Golang的channel交叉打印两个数组的操作
Apr 29 Golang
基于Golang 高并发问题的解决方案
May 08 Golang
Golang 实现获取当前函数名称和文件行号等操作
May 08 Golang
go xorm框架的使用
May 22 Golang
浅谈Go语言多态的实现与interface使用
Jun 16 Golang
Go语言应该什么情况使用指针
Jul 25 Golang
Golang并发操作中常见的读写锁详析
Aug 30 Golang
Go归并排序算法的实现方法
Apr 06 Golang
Golang并发工具Singleflight
May 06 Golang
Go缓冲channel和非缓冲channel的区别说明
Apr 25 #Golang
Go语言使用select{}阻塞main函数介绍
win10下go mod配置方式
Go语言-为什么返回值为接口类型,却返回结构体
Apr 24 #Golang
go:垃圾回收GC触发条件详解
Apr 24 #Golang
基于go interface{}==nil 的几种坑及原理分析
Apr 24 #Golang
golang interface判断为空nil的实现代码
Apr 24 #Golang
You might like
php中文字母数字验证码实现代码
2008/04/25 PHP
关于session在PHP5的配置文件中的详细设置参数说明
2011/04/20 PHP
dedecms函数分享之获取某一栏目所有子栏目
2014/05/19 PHP
php中替换字符串中的空格为逗号','的方法
2014/06/09 PHP
PHP批量修改文件名称的方法分析
2017/02/27 PHP
JavaScript中出现乱码的处理心得
2009/12/24 Javascript
jquery仿京东导航/仿淘宝商城左侧分类导航下拉菜单效果
2013/04/24 Javascript
jquery 获取 outerHtml 包含当前节点本身的代码
2014/10/30 Javascript
JavaScript中的setMilliseconds()方法使用详解
2015/06/11 Javascript
js ajaxfileupload.js上传报错的解决方法
2016/05/05 Javascript
Angular.js中$apply()和$digest()的深入理解
2016/10/13 Javascript
详解vue-resource promise兼容性问题
2017/06/20 Javascript
微信小程序列表渲染功能之列表下拉刷新及上拉加载的实现方法分析
2017/11/27 Javascript
vue webpack实用技巧总结
2018/04/24 Javascript
JavaScript数组去重算法实例小结
2018/05/07 Javascript
layui树形菜单动态遍历的例子
2019/09/23 Javascript
[02:29]DOTA2英雄基础教程 陈
2013/12/17 DOTA
[13:18]《一刀刀一天》之DOTA全时刻21:详解TI新赛制 A队再露獠牙
2014/06/24 DOTA
初步探究Python程序的执行原理
2015/04/11 Python
Python处理字符串之isspace()方法的使用
2015/05/19 Python
Django自定义插件实现网站登录验证码功能
2017/04/19 Python
pytorch中torch.max和Tensor.view函数用法详解
2020/01/03 Python
使用wxpy实现自动发送微信消息功能
2020/02/28 Python
Python matplotlib画图时图例说明(legend)放到图像外侧详解
2020/05/16 Python
python设置表格边框的具体方法
2020/07/17 Python
Python3爬虫中pyspider的安装步骤
2020/07/29 Python
Python如何使用ConfigParser读取配置文件
2020/11/12 Python
CSS3动画之利用requestAnimationFrame触发重新播放功能
2019/09/11 HTML / CSS
amazeui页面分析之登录页面的示例代码
2020/08/25 HTML / CSS
美国汽配连锁巨头Pep Boys官网:轮胎更换、汽车维修服务和汽车零部件
2017/01/14 全球购物
英国家喻户晓的家居商店:The Range
2019/03/25 全球购物
俄罗斯在线大型超市:ТутПросто
2021/01/08 全球购物
电子专业毕业生自我鉴定
2014/01/22 职场文书
给校长的建议书600字
2014/05/15 职场文书
2016年国庆节67周年活动总结
2016/04/01 职场文书
《火纹风花雪月无双》预告“神秘雇佣兵” 紫发剑客
2022/04/13 其他游戏