GoLang bytes.Buffer基础使用方法详解
作者:鲲鹏飞九万里 发布时间:2024-04-27 15:28:09
一、bytes.Buffer的基础知识
与strings.Builder
一样,bytes.Buffer
也是开箱即用的。
bytes.Buffer
类型的用途主要是作为字节序列的缓冲区。
在内部,bytes.Buffer
类型使用字节切片作为内容容器,并有一个int类型的字段作为已读计数,这个已读计数无法通过bytes.Buffer
提供的方法计算出来。
var buffer1 bytes.Buffer
contents := "Simple byte buffer for marshaling data"
// Write contents "Simple byte buffer for marshaling data"
fmt.Printf("Write contents %q\n", contents)
buffer1.WriteString(contents)
// The length of buffer: 38
fmt.Printf("The length of buffer: %d\n", buffer1.Len())
// The capacity of buffer: 64
fmt.Printf("The capacity of buffer: %d\n", buffer1.Cap())
与strings.Reader
类型的Len方法一样,buffer1的Len方法返回的也是内容容器中未被读取部分的长度,而不是其中已存内容的总长度。
Buffer 值的长度是未读内容的长度,而不是已读内容的长度。
p1 := make([]byte, 7)
n, _ := buffer1.Read(p1)
// 7 bytes were read. (call Read)
fmt.Printf("%d bytes were read. (call Read)\n", n)
// The length of buffer: 31
fmt.Printf("The length of buffer: %d\n", buffer1.Len())
// The capacity of buffer: 64
fmt.Printf("The capacity of buffer: %d\n", buffer1.Cap())
Buffer值的容量是它的内容容器(也就是那个字节切片)的容量,它只与当前值之上的写操作有关,并随着内容的写入而不断增长。
二、bytes.Buffer类型的值已读计数的作用
读取内容时,相应方法会依据已读计数找到未读内容,并在读取之后更新计数;
相应方法包括所有名称以Read开头的方法,以及Next方法和WriteTo方法。
写入内容时,如需扩容,相应方法会根据已读计数实现扩容策略;
写入时,如果没有足够的容量,就会对容器进行扩容。
扩容时,方法会在必要时,依据已读计数找到未读部分,并把其中的内容拷贝到扩展容器的头部位置。然后,方法会把已读计数值置为0。
相应方法包括所有名称以Write开头的方法,以及ReadFrom方法。
截断内容时,相应方法截断的时已读计数代表索引之后的未读部分;
截断方法Truncate,接受一个int类型的参数,表示在截断时需要保留头部多少个字节。
这里的头部是未读部分的头部,而不是内容容器的头部。
这种情况下,已读计数的值再加上参数值后得到的和,就是内容容器新的总长度。
读回退时,相应方法会使用已读计数记录回退点;
用于读回退的方法有UnreadByte和UnreadRune。这两个方法分别用于回退一个字节和回退一个Unicode字符。
回退的前提是,在调用它们之前的那一个操作必须是“读取”,并且是成功的读取,否则这些方法就只会忽略后续操作并返回一个非nil的错误值。
只有紧挨在调用ReadRune方法之后,对UnreadRune方法对调用才能够成功完成。
重置内容时,相应方法会把已读计数置为0;
导出内容时,相应方法只会导出已读计数代表的索引之后的未读部分;
Buffer值的Bytes和String方法,只会访问未读部分的内容,并返回相应的结果值。
获取长度时,相应方法会依据已读计数和内容容器的长度,计算未读部分的长度并返回;
Buffer值的Len方法返回的是内容容器未读部分的长度。
三、bytes.Buffer的扩容策略
bytes.Buffer
既可以手动扩容,也可以自动扩容。除非完全确定后续内容所需的字节数,否则让Buffer自动扩容就好了。这两种方式的扩容策略一样。
扩容策略:
判断内容容器的剩余容量,是否满足调用方的要求,是否足够容纳新的内容;
如果剩余容量满足容纳新的内容,就在当前的内容容器之上,进行长度扩容;
buf = buf[:length+need]
如果剩余容量不满足容纳新的内容,就会用新的内容容器去替代原有的内容容器,从而实现扩容;
这里有一个优化,如果当前内容容器的容量的一半,仍然大于或等于现有长度(即未读字节数)再加上另需字节数的和,即:
cap(buf)/2 >= len(buf) + need
那么扩容代码就会复用现有的内容容器,并把容器中的未读内容拷贝到它的头部位置。
这意味着,其中的已读内容,将会全部被未读内容和之后的新内容覆盖掉。
如果当前内容容器的容量小于新长度的二倍。这时,就会把原有容器中的未读内容拷贝进去,最后再用新的容器替换掉原有的容器。这个新容器将会等于原有容量的二倍,再加上另需字节数的和。
新容器的容量 = 原有容量 * 2 + 所需字节数
扩容还会把已读计数置为0。
对于处于零值状态的Buffer值来说,如果第一次扩容时另需的字节数小于等于64,那么该值就会基于一个预先定义好的、长度为64的字节数组来创建内容容器。
这种情况下,容器的容量就是64。这样做的目的是为了让Buffer值在刚被真正使用的时候,可以快速的做好准备。
四、bytes.Buffer的哪些方法会造成内容的泄露
这里的内容泄露是指,使用Buffer值的一方通过某种非标准的方式,得到本不该得到的内容。
在bytes.Buffer
中,Bytes方法和Next方法都有可能会造成内容的泄露。原因在于,它们都把基于内容容器的切片直接返回给了方法的调用方。
通过切片,我们可以直接访问和操纵它们的底层数组,不论这个切片是基于某个数组得来的,还是痛哦过对另一个切片做切片操作获得的,都是如此。
bytes.Buffer
的Bytes方法和Next方法返回的字节切片,都是通过对内容容器的切片做切片操作得到的。
contents := "ab"
buffer1 := bytes.NewBufferString(contents)
// The capacity of new buffer with contents "ab": 8
// 容量为何为8,看 runtime/string.go#stringtoslicebyte()
fmt.Printf("The capacity of new buffer with contents %q: %d\n", contents, buffer1.Cap())
unreadBytes := buffer1.Bytes()
// The unread bytes of the buffer: [97 98]
fmt.Printf("The unread bytes of the buffer: %v\n", unreadBytes)
buffer1.WriteString("cdefg")
// The capacity of new buffer with contents "ab": 8
fmt.Printf("The capacity of new buffer with contents %q: %d\n", contents, buffer1.Cap())
unreadBytes = unreadBytes[:cap(unreadBytes)]
// 基于前面的内容获取到结果值
// The unread bytes of the buffer: [97 98 99 100 101 102 103 0]
fmt.Printf("The unread bytes of the buffer: %v\n", unreadBytes)
// 操纵buffer
unreadBytes[len(unreadBytes)-2] = byte('X')
// The unread bytes of the buffer: [97 98 99 100 101 102 88 0]
fmt.Printf("The unread bytes of the buffer: %v\n", unreadBytes)
来源:https://blog.csdn.net/hefrankeleyn/article/details/129340484


猜你喜欢
- 这里首先要介绍官方文档,对python有了进一步深度的学习的大家们应该会发现,网上不管csdn或者简书上还是什么地方,教程来源基本就是官方文
- 1 、创建一个django项目使用django-admin.py startproject MyDjangoSite 参考这里2、建立视图f
- 前言本文主要给大家介绍了关于Django自定义过滤器的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍:过滤器与函数d
- 1.使用场景定时执行jmeter脚本,通过python定时器隔一段时间执行命令行命令。2.库os、datetime、threading(1)
- 前段时间因为忙一些其它的事情,分享的有些少,最近学习一下redis在Go语言开发中的应用。一、理论知识Redis是一个开源的、使用C语言编写
- 格式getopt(args, options[, long_options])1.args表示要解析的参数. 2.options表示脚本要识
- 本文实例为大家分享了vue简单实现购物车结算的具体代码,供大家参考,具体内容如下样式没有写<template> <
- 函数绑定(Function binding)很有可能是你在开始使用JavaScript时最少关注的一点,但是当你意识到你需要一个解决方案来解
- 如下所示:a = [99,1,2,1,3,4]# 集合存储重复数据b=set()for i in a: if a.count(i
- 这篇文章主要介绍了Python urlopen()和urlretrieve()用法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作
- Onunload,onbeforeunload都是在刷新或关闭时调用,可以在<script>脚本中通过window.onunlo
- 前言: 在爬虫过程中,我们可能需要重复的爬取同一个网站,为了避免重复的数据存入我们的数据库中 通过实现增量去重 去解决这一问题 本文还针对了
- Python常用的数据结构,有如下几种。但是我们用的最多的,还是字符串、列表、字典这3种。其实学习任何一门编程语言,最基础的就是学习它的数据
- 远程服务器配置可以使得数据库管理员在服务器以外的主机上连接到一个SQL Server实例,以便管理员在没有建立单据连接的情况下在其他的SQL
- Pillow图片格式转换Pillow 库支持多种图片格式,您可以直接使用 open() 方法来读取图片,并且无须考虑图片是何种类型。Pill
- 本文实例讲述了mysql存储过程之游标(DECLARE)原理与用法。分享给大家供大家参考,具体如下:我们在处理存储过程中的结果集时,可以使用
- 本文为大家分享了pygame游戏之旅的第7篇,供大家参考,具体内容如下对car和障碍的宽高进行比较然后打印即可:if y < thin
- 本文介绍MySQL与Redis缓存的同步的两种方案方案1:通过MySQL自动同步刷新Redis,MySQL触发器+UDF函数实现方案2:解析
- Book表的数据显示id title price publish_id2 Linux &nb
- 本文列举了兼容 IE 和 FF 的换行 CSS 推荐样式,详细介绍了word-wrap同word-break的区别。兼容 IE 和 FF 的