strings 包主要面向的是 Unicode 字符和经过 UTF-8 编码的字符串,而 bytes 包面对的主要是字节和字节切片。
bytes.Buffer 值的长度是未读内容的长度,而不是已存内容的总长度。
strings.Reader 的 Size 方法可给出内容长度的值,用内容长度的值减去未读部分的长度,就可以方便得到已读计数。
但 bytes.Buffer 没有 Size 方法,只有 Cap 方法,而 Cap 提供的容器的容量,而不是内容的长度,所以很难估算 Bufffer 的已读计数。
package main
import (
"bytes"
"fmt"
)
func main() {
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()
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())
}
bytes.Buffer 的扩容策略
package main
import (
"bytes"
"fmt"
)
func main() {
var contents string
buffer1 := bytes.NewBufferString(contents)
fmt.Printf("The length of new buffer with contents %q: %d\n",
contents, buffer1.Len())
fmt.Printf("The capacity of new buffer with contents %q: %d\n",
contents, buffer1.Cap())
fmt.Println()
// 容器剩余容量不够了,且容器容量 < 新长度的二倍,那么用新的容器替代原有的容器
// 新容器的容量 = 2 * 原容器容量 + 所需字节数
contents = "12345"
fmt.Printf("Write contents %q ...\n", contents)
buffer1.WriteString(contents)
fmt.Printf("The length of buffer: %d\n", buffer1.Len()) // 0
fmt.Printf("The capacity of buffer: %d\n", buffer1.Cap()) // 0
fmt.Println()
contents = "67"
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()
// 容器容量够,在当前容器上进行长度扩充
contents = "89"
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()
l := make([]byte, 9)
buffer1.Read(l) // 读了 9 个字节
fmt.Printf("The length of buffer: %d\n", buffer1.Len())
fmt.Printf("The capacity of buffer: %d\n", buffer1.Cap())
fmt.Println()
// 容器剩余容量不够了,但如果 cap(b.buf)/2 >= len(b.buf) + need
// 则复用现有容器,并把容器中的未读内容拷贝到头部位置,覆盖已读内容
contents = "1011" // 如果没有读操作的话,容器中的总字节数为 13,大于容量 12,需要扩容
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.Print("\n\n")
}
bytes.Buffer 中容易导致内容泄露的方法
内容泄露指通过某种非标准的方法得到本不该得到的内容。
Bytes 和 Next 方法可能会造成内容的泄露,因为它们都把基于内容容器的切片值直接返回给了方法的调用方。
package main
import (
"bytes"
"fmt"
)
func main() {
contents := "ab"
buffer1 := bytes.NewBufferString(contents)
fmt.Printf("The capacity of new buffer with contents %q: %d\n",
contents, buffer1.Cap()) // 容量为 8
fmt.Println()
unreadBytes := buffer1.Bytes() // 未读内容为 [97 98]
fmt.Printf("The unread bytes of the buffer: %v\n", unreadBytes)
fmt.Println()
contents = "cdefg"
fmt.Printf("Write contents %q ...\n", contents)
buffer1.WriteString(contents)
fmt.Printf("The capacity of buffer: %d\n", buffer1.Cap())
fmt.Println()
// 内容泄露,不需要再次调用 Bytes() 就可以得到未读内容
unreadBytes = unreadBytes[:cap(unreadBytes)] // 基于前面的获取到的结果值
fmt.Printf("The unread bytes of the buffer: %v\n", unreadBytes)
fmt.Println()
// 在外部随意操作 buffer1 的内容
value := byte('X')
fmt.Printf("Set a byte in the unread bytes to %v ...\n", value)
unreadBytes[len(unreadBytes)-2] = value
fmt.Printf("The unread bytes of the buffer: %v\n", buffer1.Bytes())
fmt.Println()
// 不过,真正扩容后就无法在外部修改了,因为底层数组被重写设定了
contents = "hijklmn"
fmt.Printf("Write contents %q ...\n", contents)
buffer1.WriteString(contents)
fmt.Printf("The capacity of buffer: %d\n", buffer1.Cap())
fmt.Println()
unreadBytes = unreadBytes[:cap(unreadBytes)]
fmt.Printf("The unread bytes of the buffer: %v\n", unreadBytes)
fmt.Print("\n\n")
}