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 相关文章推荐
golang interface判断为空nil的实现代码
Apr 24 Golang
go原生库的中bytes.Buffer用法
Apr 25 Golang
go语言中json数据的读取和写出操作
Apr 28 Golang
go语言中GOPATH GOROOT的作用和设置方式
May 05 Golang
Golang 实现获取当前函数名称和文件行号等操作
May 08 Golang
基于Go语言构建RESTful API服务
Jul 25 Golang
golang 语言中错误处理机制
Aug 30 Golang
详解Golang如何优雅的终止一个服务
Mar 21 Golang
golang操作rocketmq的示例代码
Apr 06 Golang
golang生成并解析JSON
Apr 14 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中$this-&amp;gt;含义分析
2009/11/29 PHP
ThinkPHP分页类使用详解
2014/03/05 PHP
django中的ajax组件教程详解
2018/10/18 PHP
用js实现的模拟jquery的animate自定义动画(2.5K)
2010/07/20 Javascript
基于jQuery实现模拟页面加载进度条
2013/04/01 Javascript
js onclick事件传参讲解
2013/11/06 Javascript
jquery淡化版banner异步图片文字效果切换图片特效
2014/04/08 Javascript
js由下向上不断上升冒气泡效果实例
2015/05/07 Javascript
node.js微信公众平台开发教程
2016/03/04 Javascript
angularJS Provider、factory、service详解及实例代码
2016/09/21 Javascript
angular+ionic 的app上拉加载更新数据实现方法
2017/01/16 Javascript
JavaScript使用readAsDataURL读取图像文件
2017/05/10 Javascript
JavaScript箭头函数_动力节点Java学院整理
2017/06/28 Javascript
使用ionic(选项卡栏tab) icon(图标) ionic上拉菜单(ActionSheet) 实现通讯录界面切换实例代码
2017/10/20 Javascript
使用 Vue 绑定单个或多个 Class 名的实例代码
2018/01/08 Javascript
Vue对象赋值视图不更新问题及解决方法
2019/06/03 Javascript
JavaScript通如何过RGraph实现动态仪表盘
2020/10/15 Javascript
Python统计日志中每个IP出现次数的方法
2015/07/06 Python
python数组过滤实现方法
2015/07/27 Python
Python 错误和异常代码详解
2018/01/29 Python
使用python实现BLAST
2018/02/12 Python
对python过滤器和lambda函数的用法详解
2019/01/21 Python
Python基于class()实现面向对象原理详解
2020/03/26 Python
深入了解canvas在移动端绘制模糊的问题解决
2019/04/30 HTML / CSS
美国最顶级的精品店之一:Hampden Clothing
2016/12/22 全球购物
彼得罗夫美国官网:Peter Thomas Roth美国(青瓜面膜)
2017/11/05 全球购物
新西兰廉价汽车租赁:Snap Rentals
2018/09/14 全球购物
高级运动鞋:GREATS
2019/07/19 全球购物
eharmony澳大利亚:网上约会服务
2020/02/29 全球购物
农村婚礼证婚词
2014/01/10 职场文书
旅游专业职业生涯规划范文
2014/01/13 职场文书
卖房协议书
2014/04/11 职场文书
保管员岗位职责
2015/02/14 职场文书
Lombok的详细使用及优缺点总结
2021/07/15 Java/Android
OpenCV绘制圆端矩形的示例代码
2021/08/30 Python
mysql的单列多值存储实例详解
2022/04/05 MySQL