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判断key是否在map中的代码
Apr 24 Golang
go语言中json数据的读取和写出操作
Apr 28 Golang
goland设置颜色和字体的操作
May 05 Golang
golang gopm get -g -v 无法获取第三方库的解决方案
May 05 Golang
go web 预防跨站脚本的实现方式
Jun 11 Golang
Go 通过结构struct实现接口interface的问题
Oct 05 Golang
Golang 并发下的问题定位及解决方案
Mar 16 Golang
Go语言实现一个简单的并发聊天室的项目实战
Mar 18 Golang
Go语言grpc和protobuf
Apr 13 Golang
golang连接MySQl使用sqlx库
Apr 14 Golang
Go获取两个时区的时间差
Apr 20 Golang
Go gorilla/sessions库安装使用
Aug 14 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将session放入memcached的设置方法
2014/02/14 PHP
去除php注释和去除空格函数分享
2014/03/13 PHP
php按百分比生成缩略图的代码分享
2014/05/10 PHP
PHP的魔术常量__METHOD__简介
2014/07/08 PHP
jQuery EasyUI 中文API Layout(Tabs)
2010/04/27 Javascript
jQuery-Easyui 1.2 实现多层菜单效果的代码
2012/01/13 Javascript
jquery validate添加自定义验证规则(验证邮箱 邮政编码)
2013/12/04 Javascript
html的DOM中Event对象onabort事件用法实例
2015/01/21 Javascript
javascript模拟php函数in_array
2015/04/27 Javascript
Jquery结合HTML5实现文件上传
2015/06/25 Javascript
jQuery弹层插件jquery.fancybox.js用法实例
2016/01/22 Javascript
js给table赋值的实例代码
2016/10/13 Javascript
理解javascript中的Function.prototype.bind的方法
2017/02/03 Javascript
微信小程序用户信息encryptedData详解
2018/08/24 Javascript
vue项目中在外部js文件中直接调用vue实例的方法比如说this
2019/04/28 Javascript
[48:05]2018DOTA2亚洲邀请赛 3.31 小组赛 B组 VGJ.T vs VP
2018/03/31 DOTA
[01:51]开启你的城市传奇 完美世界城市挑战赛开始报名
2018/10/09 DOTA
Python计算三角函数之asin()方法的使用
2015/05/15 Python
Python中函数的参数定义和可变参数用法实例分析
2015/06/04 Python
Django应用程序中如何发送电子邮件详解
2017/02/04 Python
利用标准库fractions模块让Python支持分数类型的方法详解
2017/08/11 Python
python实现本地图片转存并重命名的示例代码
2018/10/27 Python
python脚本开机自启的实现方法
2019/06/28 Python
Java ExcutorService优雅关闭方式解析
2020/05/30 Python
python语言的优势是什么
2020/06/17 Python
python 绘制正态曲线的示例
2020/09/24 Python
Python中的面向接口编程示例详解
2021/01/17 Python
css3加js做一个简单的3D行星运转效果实例代码
2017/01/18 HTML / CSS
美国最大的城市服装和运动鞋零售商:Jimmy Jazz
2016/11/19 全球购物
初三学生个人自我评定
2014/04/06 职场文书
教师节演讲稿
2014/05/06 职场文书
城市规划应届生推荐信
2014/09/08 职场文书
普通党员四风问题对照检查材料
2014/09/27 职场文书
大学生实习推荐信
2015/03/27 职场文书
消防演习通知
2015/04/25 职场文书
Django使用redis配置缓存的方法
2021/06/01 Redis