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语言使用select{}阻塞main函数介绍
Apr 25 Golang
golang 如何通过反射创建新对象
Apr 28 Golang
Go语言中break label与goto label的区别
Apr 28 Golang
Golang之sync.Pool使用详解
May 06 Golang
go开发alertmanger实现钉钉报警
Jul 16 Golang
go goroutine 怎样进行错误处理
Jul 16 Golang
使用GO语言实现Mysql数据库CURD的简单示例
Aug 07 Golang
Go 语言中 20 个占位符的整理
Oct 16 Golang
实现GO语言对数组切片去重
Apr 20 Golang
Go语言入门exec的基本使用
May 20 Golang
Go结合Gin导出Mysql数据到Excel表格
Aug 05 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
免费手机号码归属地API查询接口和PHP使用实例分享
2014/04/10 PHP
PHP实现的简单操作SQLite数据库类与用法示例
2017/06/19 PHP
js 单引号 传递方法
2009/06/22 Javascript
对象特征检测法判断浏览器对javascript对象的支持
2009/07/25 Javascript
Array.prototype 的泛型应用分析
2010/04/30 Javascript
javascript学习笔记(十六) 系统对话框(alert、confirm、prompt)
2012/06/20 Javascript
jquery在IE、FF浏览器的差别详细探讨
2013/04/28 Javascript
多种方法实现JS动态添加事件
2013/11/01 Javascript
Javascript闭包用法实例分析
2015/01/23 Javascript
JS实现网页上随机产生超链接地址的方法
2015/11/09 Javascript
全面解析Angular中$Apply()及$Digest()的区别
2016/08/04 Javascript
原生js开发的日历插件
2017/02/04 Javascript
angularjs利用directive实现移动端自定义软键盘的示例
2017/09/20 Javascript
React中上传图片到七牛的示例代码
2017/10/10 Javascript
vue2组件之select2调用的示例代码
2017/10/12 Javascript
浅析从vue源码看观察者模式
2018/01/29 Javascript
Vue Element使用icon图标教程详解(第三方)
2018/02/07 Javascript
基于Vue实现拖拽效果
2018/04/27 Javascript
微信小程序中的video视频实现 自定义播放按钮、封面图、视频封面上文案
2020/01/02 Javascript
es6函数之rest参数用法实例分析
2020/04/18 Javascript
javascript实现前端分页效果
2020/06/24 Javascript
python遍历文件夹下所有excel文件
2018/01/03 Python
利用python为运维人员写一个监控脚本
2018/03/25 Python
python实现输入数字的连续加减方法
2018/06/22 Python
Django如何将URL映射到视图
2019/07/29 Python
python中numpy.empty()函数实例讲解
2021/02/05 Python
英国知名化妆品网站:Revolution Beauty(原TAM Beauty)
2018/02/28 全球购物
美国电力供应商店/电气批发商:USESI
2018/10/12 全球购物
应届毕业生简历自我评价
2014/01/31 职场文书
离婚协议书范本2014
2014/10/27 职场文书
英语读书笔记
2015/07/02 职场文书
2016年情人节问候语
2015/11/11 职场文书
PHP基本语法
2021/03/31 PHP
Python Socket编程详解
2021/04/25 Python
带你了解CSS基础知识,样式
2021/07/21 HTML / CSS
超越Nginx的Web服务器caddy优雅用法
2022/06/21 Servers