strings包与bytes包的对比

  • strings包面向的是 Unicode 字符和经过 UTF-8 编码的字符串
  • bytes包面向的主要是字节和字节切片
  • strings.Builder 与 bytes.Builder 一样都是开箱即用的
  • strings.Builder 只能拼接和导出字符串,bytes.Builder 不但可以拼接、截取其中的字节序列,以各种形式导出其中的内容,还可以顺序读取其中的子序列
  • bytes.Builder 是集读、写功能于一身的数据类型(这些也基本上都是一个缓冲区应该拥有的功能)
  • strings.Reader 可以计算出已读计数, bytes.Buffer 不可以计算(但不意味着没有
  1. strings.Reader使用的Size方法计算出内容长度的值,然后减去未读部分的长度
  2. bytes.Buffer没有Size方法,只有Cap方法(表示内容容器的容量,而是其中内容的长度)
  • strings.Reader 和 bytes.Buffer 中的Len方法都只会去访问未读部分的内容,并返回相应的结果值

bytes.Buffer 类型值记录的已读计数,在其中起到怎样的作用

  • 读取内容时,相应额方法会根据已读计数找到未读部分,并在读取后更新计数
  • 写入内容时,如需扩容,相应的方法会根据已读计数实现扩容策略
  • 截取内容时,相应方法截掉的是已读计数代表索引之后的未读部分
  • 读回退时,相应方法需要用已读计数记录回退点
  • 重置内容时,相应的方法会把已读计数置为0
  • 导出内容时,相应的方法只会导出已读计数代表的索引之后的维度部分
  • 获取长度时,相应的方法会依据已读计数和内容容器的长度,计算出未读部分的长度值

bytes.Buffer 的扩容策略

1. 如果当前内容容器容量的一半,仍然大于或等于其现有长度再加上另需的字节数的和

  • 扩容的代码会复用现有的内容容器,并把容器中的未读内容拷贝到它的头部位置。这也意味着其中的已读内容将会全部被未读内容和之后的新内容覆盖掉

    2. 如果当前内容容器容量的一半,小于现有长度再加上另需的字节数的和

  • 扩容代码创建一个新的内容容器,并把原有容器中的未读部分拷贝进去

  • 最后在用新的内容容器替换掉原有的容器
  1. package main
  2. import (
  3. "bytes"
  4. "fmt"
  5. )
  6. func main() {
  7. // Demo01
  8. var buffer1 bytes.Buffer
  9. contents := "Simple byte buffer for marshaling data."
  10. fmt.Printf("Writing contents %q ...\n", contents)
  11. buffer1.WriteString(contents)
  12. fmt.Printf("The length of buffer: %d\n", buffer1.Len())
  13. fmt.Printf("The capacity of buffer: %d\n", buffer1.Cap())
  14. fmt.Println()
  15. // Demo2
  16. p1 := make([]byte, 7)
  17. n, _ := buffer1.Read(p1)
  18. fmt.Printf("%d bytes were read. (Call Read)\n", n)
  19. fmt.Printf("The length of buffer: %d\n", buffer1.Len())
  20. fmt.Printf("The capacity of buffer: %d\n", buffer1.Cap())
  21. // Demo3
  22. contents = "abcdefghijk"
  23. buffer2 := bytes.NewBufferString(contents)
  24. fmt.Printf("The length of buffer: %d\n", buffer2.Len())
  25. fmt.Printf("The capacity of buffer: %d\n", buffer2.Cap())
  26. fmt.Println()
  27. }