Golang bufio详细讲解


Posted in Golang onApril 21, 2022

bufio 包介绍 

bufio包实现了有缓冲的I/O。它包装一个io.Reader或io.Writer接口对象,创建另一个也实现了该接口,且同时还提供了缓冲和一些文本I/O的帮助函数的对象。

golang bufio

当频繁地对少量数据读写时会占用IO,造成性能问题。golang的bufio库使用缓存来一次性进行大块数据的读写,以此降低IO系统调用,提升性能。

在Transport中可以设置一个名为WriteBufferSize的参数,该参数指定了底层(Transport.dialConn)写buffer的大小。

tr := &http.Transport{
		WriteBufferSize:     64 * 1024,
	}
pconn.br = bufio.NewReaderSize(pconn, t.readBufferSize())
	pconn.bw = bufio.NewWriterSize(persistConnWriter{pconn}, t.writeBufferSize())

使用bufio进行写

可以使用bufio.NewWriter初始化一个大小为4096字节的Writer(见下),或使用bufio.NewWriterSize初始化一个指定大小的Writer

Writer中的主要参数为缓存区buf,缓存区中的数据偏移量n以及写入接口wr

type Writer struct {
	err error
	buf []byte
	n   int
	wr  io.Writer
}

bufio.Writer方法可以一次性写入缓存中的数据,通常有如下三种情况:

  • 缓存中满数据
  • 缓存中仍有空间
  • 待写入的数据大于缓存的大小

缓存中满数据

当缓存中满数据时,会执行写操作。

缓存中仍有空间

如果缓存中仍有数据,则不会执行写入动作,除非调用Flush()方法。

待写入的数据大于缓存的大小

由于此时缓存无法缓存足够的数据,此时会跳过缓存直接执行写操作

type Writer int
func (*Writer) Write(p []byte) (n int, err error) {
	fmt.Printf("Writing: %s\n", p)
	return len(p), nil
}
func main() {
	w := new(Writer)
	bw1 := bufio.NewWriterSize(w, 4)
	// Case 1: Writing to buffer until full
	bw1.Write([]byte{'1'})
	bw1.Write([]byte{'2'})
	bw1.Write([]byte{'3'})
	bw1.Write([]byte{'4'}) // write - buffer is full
	// Case 2: Buffer has space
    bw1.Write([]byte{'5'}) //此时buffer中无法容纳更多的数据,执行写操作,写入 []byte{'1','2','3','4'}
	err = bw1.Flush() // forcefully write remaining
	if err != nil {
		panic(err)
	}
	// Case 3: (too) large write for buffer
	// Will skip buffer and write directly
	bw1.Write([]byte("12345")) //buffer不足,直接执行写操作
//结果:
Writing: 1234
Writing: 5
Writing: 12345

缓存重用

申请缓存对性能是有损耗的,可以使用Reset方法重置缓存,其内部只是将Writer的数据偏移量n置0。

wr := new(Writer)
bw := bufio.NewWriterSize(wr,2) 
bw.Reset(wr)

获取缓存的可用空间数

Available()方法可以返回缓存的可用空间数,即len(Writer.buf)-Writer.n

使用bufio进行读

与用于写数据的Writer类似,读数据也有一个Reader,可以使用NewReader初始化一个大小为4096字节的Reader,或使用NewReaderSize初始化一个指定大小的Reader(要求最小为16字节)。Reader也有一个记录偏移量的变量r

type Reader struct {
	buf          []byte
	rd           io.Reader // reader provided by the client
	r, w         int       // buf read and write positions
	err          error
	lastByte     int // last byte read for UnreadByte; -1 means invalid
	lastRuneSize int // size of last rune read for UnreadRune; -1 means invalid
}

Peek

该方法会返回buf中的前n个字节的内容,但与Read操作不同的是,它不会消费缓存中的数据,即不会增加数据偏移量,因此通常也会用于判断是否读取结束(EOF)。通常有如下几种情况:

  • 如果peak的值小于缓存大小,则返回相应的内容
  • 如果peak的值大于缓存大小,则返回bufio.ErrBufferFull错误
  • 如果peak的值包含EOF且小于缓存大小,则返回EOF

Read

将数据读取到p,涉及将数据从缓存拷贝到p

func (b *Reader) Read(p []byte) (n int, err error)

ReadSlice

该方法会读从缓存读取数据,直到遇到第一个delim。如果缓存中没有delim,则返回EOF,如果查询的长度超过了缓存大小,则返回 io.ErrBufferFull 错误。

func (b *Reader) ReadSlice(delim byte) (line []byte, err error)

例如delim',',则下面会返回的内容为1234,

r := strings.NewReader("1234,567")
rb := bufio.NewReaderSize(r, 20)
fmt.Println(rb.ReadSlice(','))
// 结果:[49 50 51 52 44] <nil>

注意:ReadSlice返回的是原始缓存中的内容,如果针对缓存作并发操作,则返回的内容有可能被其他操作覆盖。因此在官方注释里面有写,建议使用ReadBytesReadString。但ReadBytesReadString涉及内存申请和拷贝,因此会影响性能。在追求高性能的场景下,建议外部使用sync.pool来提供缓存。

// Because the data returned from ReadSlice will be overwritten
// by the next I/O operation, most clients should use
// ReadBytes or ReadString instead.

ReadLine

ReadLine() (line []byte, isPrefix bool, err error)

ReadLine底层用到了ReadSlice,但在返回时会移除\n 或\r\n。需要注意的是,如果切片中没有找到换行符,则不会返回EOF或io.ErrBufferFull 错误,相反,它会将isPrefix置为true

ReadBytes

ReadSlice类似,但它会返回一个新的切片,因此便于并发使用。如果找不到delimReadBytes会返回io.EOF

func (b *Reader) ReadBytes(delim byte) ([]byte, error)

Scanner

scanner可以不断将数据读取到缓存(默认64*1024字节)。

rb := strings.NewReader("12345678901234567890")
	scanner := bufio.NewScanner(rb)
	for scanner.Scan() {
		fmt.Printf("Token (Scanner): %q\n", scanner.Text())
	}
	// 结果:Token (Scanner): "12345678901234567890"

参考

how-to-read-and-write-with-golang-bufio

到此这篇关于golang bufio解析的文章就介绍到这了!


Tags in this post...

Golang 相关文章推荐
一文读懂go中semaphore(信号量)源码
Apr 03 Golang
用golang如何替换某个文件中的字符串
Apr 25 Golang
go语言中json数据的读取和写出操作
Apr 28 Golang
Go语言中break label与goto label的区别
Apr 28 Golang
彻底理解golang中什么是nil
Apr 29 Golang
浅谈golang 中time.After释放的问题
May 05 Golang
golang gopm get -g -v 无法获取第三方库的解决方案
May 05 Golang
Go语言读取txt文档的操作方法
Jan 22 Golang
GO语言字符串处理函数之处理Strings包
Apr 14 Golang
详解Go语言中Get/Post请求测试
Jun 01 Golang
GoFrame框架数据校验之校验结果Error接口对象
Jun 21 Golang
Go语言编译原理之源码调试
Aug 05 Golang
Go获取两个时区的时间差
Apr 20 #Golang
Golang jwt身份认证
实现GO语言对数组切片去重
Apr 20 #Golang
Golang日志包的使用
Apr 20 #Golang
Golang获取List列表元素的四种方式
Apr 20 #Golang
Golang 对es的操作实例
Apr 20 #Golang
Golang 遍历二叉树
Apr 19 #Golang
You might like
PHP实现下载功能的代码
2012/09/29 PHP
[原创]php获取数组中键值最大数组项的索引值
2015/03/17 PHP
php中array_unshift()修改数组key注意事项分析
2016/05/16 PHP
Laravel框架实现的上传图片到七牛功能详解
2019/09/06 PHP
php反序列化长度变化尾部字符串逃逸(0CTF-2016-piapiapia)
2020/02/15 PHP
安装PHP扩展时解压官方 tgz 文件后没有configure文件无法进行配置编译的问题
2020/08/26 PHP
用js判断用户浏览器是否是XP SP2的IE6
2007/03/08 Javascript
js 页面执行时间计算代码
2009/03/04 Javascript
jquery获取ASP.NET服务器端控件dropdownlist和radiobuttonlist生成客户端HTML标签后的value和text值
2010/06/28 Javascript
js与C#进行时间戳转换
2014/11/14 Javascript
谈谈PHP中相对路径的问题与绝对路径的使用
2016/08/16 Javascript
jquery将标签元素的高设为屏幕的百分比
2017/04/19 jQuery
详细AngularJs4的图片剪裁组件的实例
2017/07/12 Javascript
元素全屏的设置与监听实例
2017/11/28 Javascript
Swiper自定义分页器使用详解
2017/12/28 Javascript
nodejs 十六进制字符串型数据与btye型数据相互转换
2018/07/30 NodeJs
vue使用v-if v-show页面闪烁,div闪现的解决方法
2018/10/12 Javascript
浅谈python对象数据的读写权限
2016/09/12 Python
PyQt5+Caffe+Opencv搭建人脸识别登录界面
2019/08/28 Python
Python序列化pickle模块使用详解
2020/03/05 Python
大女孩胸罩:Big Girls Bras
2016/12/15 全球购物
新西兰优惠网站:Treat Me
2019/07/04 全球购物
G-Form护具官方网站:美国运动保护装备
2019/09/04 全球购物
ASOS西班牙官网:英国在线时尚和美容零售商
2020/01/10 全球购物
大学生求职简历的自我评价
2013/10/14 职场文书
历史系自荐信范文
2013/12/24 职场文书
旷课检讨书3000字
2014/02/04 职场文书
高中学生评语大全
2014/04/25 职场文书
供应链金融服务方案
2014/05/25 职场文书
社区戒毒工作方案
2014/06/04 职场文书
领导干部作风整顿个人剖析材料
2014/10/11 职场文书
巾帼文明岗汇报材料
2014/12/24 职场文书
辞职信范文大全
2015/03/02 职场文书
银行柜员工作心得体会
2016/01/23 职场文书
励志语录:时光飞逝,请学会珍惜所有的人和事
2020/01/16 职场文书
「魔导具师妲莉亚永不妥协~从今天开始的自由职人生活~」1、2卷发售宣传CM公开
2022/03/21 日漫