Go语言核心36讲(Go语言实战与应用十六)--学习笔记 (2)

通过上面的典型回答,我们已经能够体会到已读计数在bytes.Buffer类型,及其方法中的重要性了。没错,bytes.Buffer的绝大多数方法都用到了已读计数,而且都是非用不可。

在读取内容的时候,相应方法会先根据已读计数,判断一下内容容器中是否还有未读的内容。如果有,那么它就会从已读计数代表的索引处开始读取。

在读取完成后,它还会及时地更新已读计数。也就是说,它会记录一下又有多少个字节被读取了。这里所说的相应方法包括了所有名称以Read开头的方法,以及Next方法和WriteTo方法。

在写入内容的时候,绝大多数的相应方法都会先检查当前的内容容器,是否有足够的容量容纳新的内容。如果没有,那么它们就会对内容容器进行扩容。

在扩容的时候,方法会在必要时,依据已读计数找到未读部分,并把其中的内容拷贝到扩容后内容容器的头部位置。

然后,方法将会把已读计数的值置为0,以表示下一次读取需要从内容容器的第一个字节开始。用于写入内容的相应方法,包括了所有名称以Write开头的方法,以及ReadFrom方法。

用于截断内容的方法Truncate,会让很多对bytes.Buffer不太了解的程序开发者迷惑。 它会接受一个int类型的参数,这个参数的值代表了:在截断时需要保留头部的多少个字节。

不过,需要注意的是,这里说的头部指的并不是内容容器的头部,而是其中的未读部分的头部。头部的起始索引正是由已读计数的值表示的。因此,在这种情况下,已读计数的值再加上参数值后得到的和,就是内容容器新的总长度。

在bytes.Buffer中,用于读回退的方法有UnreadByte和UnreadRune。 这两个方法分别用于回退一个字节和回退一个 Unicode 字符。调用它们一般都是为了退回在上一次被读取内容末尾的那个分隔符,或者为重新读取前一个字节或字符做准备。

不过,退回的前提是,在调用它们之前的那一个操作必须是“读取”,并且是成功的读取,否则这些方法就只能忽略后续操作并返回一个非nil的错误值。

UnreadByte方法的做法比较简单,把已读计数的值减1就好了。而UnreadRune方法需要从已读计数中减去的,是上一次被读取的 Unicode 字符所占用的字节数。

这个字节数由bytes.Buffer的另一个字段负责存储,它在这里的有效取值范围是[1, 4]。只有ReadRune方法才会把这个字段的值设定在此范围之内。

由此可见,只有紧接在调用ReadRune方法之后,对UnreadRune方法的调用才能够成功完成。该方法明显比UnreadByte方法的适用面更窄。

我在前面说过,bytes.Buffer的Len方法返回的是内容容器中未读部分的长度,而不是其中已存内容的总长度(即:内容长度)。

而该类型的Bytes方法和String方法的行为,与Len方法是保持一致的。前两个方法只会去访问未读部分中的内容,并返回相应的结果值。

在我们剖析了所有的相关方法之后,可以这样来总结:在已读计数代表的索引之前的那些内容,永远都是已经被读过的,它们几乎没有机会再次被读取。

不过,这些已读内容所在的内存空间可能会被存入新的内容。这一般都是由于重置或者扩充内容容器导致的。这时,已读计数一定会被置为0,从而再次指向内容容器中的第一个字节。这有时候也是为了避免内存分配和重用内存空间。

总结

总结一下,bytes.Buffer是一个集读、写功能于一身的数据类型。它非常适合作为字节序列的缓冲区。我们会在下一篇文章中继续对 bytes.Buffer 的知识进行延展。

package main import ( "bytes" "fmt" ) func main() { // 示例1。 var buffer1 bytes.Buffer contents := "Simple byte buffer for marshaling data." fmt.Printf("Write contents %q ...\n", contents) buffer1.WriteString(contents) fmt.Printf("The length of buffer: %d\n", buffer1.Len()) fmt.Printf("The capacity of buffer: %d\n", buffer1.Cap()) fmt.Println() // 示例2。 p1 := make([]byte, 7) n, _ := buffer1.Read(p1) fmt.Printf("%d bytes were read. (call Read)\n", n) fmt.Printf("The length of buffer: %d\n", buffer1.Len()) fmt.Printf("The capacity of buffer: %d\n", buffer1.Cap()) } 笔记源码

https://github.com/MingsonZheng/go-core-demo

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

欢迎转载、使用、重新发布,但务必保留文章署名 郑子铭 (包含链接: ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwsxgz.html