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 Gin实现文件上传下载的示例代码
Apr 02 Golang
golang http使用踩过的坑与填坑指南
Apr 27 Golang
Go语言 go程释放操作(退出/销毁)
Apr 30 Golang
go类型转换及与C的类型转换方式
May 05 Golang
Go 自定义package包设置与导入操作
May 06 Golang
基于Golang 高并发问题的解决方案
May 08 Golang
Go语言实现Snowflake雪花算法
Jun 08 Golang
Golang生成Excel文档的方法步骤
Jun 09 Golang
K8s部署发布Golang应用程序的实现方法
Jul 16 Golang
go开发alertmanger实现钉钉报警
Jul 16 Golang
golang操作redis的客户端包有多个比如redigo、go-redis
Apr 14 Golang
深入理解 Golang 的字符串
May 04 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文件上传实例详解!!!
2007/01/02 PHP
Discuz! Passport 通行证整合
2008/03/27 PHP
Linux下安装PHP MSSQL扩展教程
2014/10/24 PHP
PHP引用返回用法示例
2016/05/28 PHP
thinkphp框架表单数组实现图片批量上传功能示例
2020/04/04 PHP
纯js实现的论坛常用的运行代码的效果
2008/07/15 Javascript
jquery 学习笔记一
2010/04/07 Javascript
jquery中ajax使用error调试错误的方法
2015/02/08 Javascript
jquery实现的缩略图预览滑块实例
2015/06/25 Javascript
js获取及修改网页背景色和字体色的方法
2015/12/29 Javascript
移动端H5开发 Turn.js实现很棒的翻书效果
2016/06/20 Javascript
Boostrap实现的登录界面实例代码
2016/10/09 Javascript
微信小程序 教程之WXSS
2016/10/18 Javascript
jQuery设置Easyui校验规则(推荐)
2016/11/21 Javascript
Vue.js系列之项目结构说明(2)
2017/01/03 Javascript
ES6中Proxy与Reflect实现重载(overload)的方法
2017/03/30 Javascript
关于JavaScript中forEach和each用法浅析
2017/07/27 Javascript
最新Javascript程序员面试试题和解题方法
2017/11/23 Javascript
在vue项目中安装使用Mint-UI的方法
2017/12/27 Javascript
Vue+ElementUI实现表单动态渲染、可视化配置的方法
2018/03/07 Javascript
vue使用vue-i18n实现国际化的实现代码
2018/04/08 Javascript
解析vue路由异步组件和懒加载案例
2018/06/08 Javascript
webpack4.x下babel的安装、配置及使用详解
2019/03/07 Javascript
JS中的算法与数据结构之列表(List)实例详解
2019/08/16 Javascript
前端vue-cli项目中使用img图片和background背景图的几种方法
2019/11/13 Javascript
js回调函数仿360开机
2019/12/26 Javascript
微信小程序手动添加收货地址省市区联动
2020/05/18 Javascript
jQuery使用jsonp实现百度搜索的示例代码
2020/07/08 jQuery
js定时器出现第一次延迟的原因及解决方法
2021/01/04 Javascript
给Python学习者的文件读写指南(含基础与进阶)
2020/01/29 Python
Canvas实现保存图片到本地的示例代码
2018/06/28 HTML / CSS
巴西女装购物网站:Eclectic
2018/04/24 全球购物
美体小铺法国官方网站:The Body Shop法国
2020/06/04 全球购物
党的群众路线教育实践活动学习笔记范文
2014/11/06 职场文书
预备党员入党思想汇报(范文)
2019/08/14 职场文书
redis实现排行榜功能
2021/05/24 Redis