Golang bytes.buffer详解
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Golang bytes.buffer详解相关的知识,希望对你有一定的参考价值。
参考技术A Buffer 介绍
Buffer 是 bytes 包中的一个 type Buffer struct…
A buffer is a variable-sized buffer of bytes with Read and Write methods. The zero value for Buffer is an empty buffer ready to use.
(是一个变长的 buffer,具有 Read 和Write 方法。 Buffer 的 零值 是一个 空的 buffer,但是可以使用)
Buffer 就像一个集装箱容器,可以存东西,取东西(存取数据)
创建缓冲器
输出
写入到缓冲器
buffer在new的时候是空的,也是可以直接Write的
Write
结果
WriteString
结果
WriteByte
WriteRune
结果
从缓冲器中写出
读出缓冲器
Read
ReadByte
返回缓冲器头部的第一个byte
ReadRun
ReadRune方法,返回缓冲器头部的第一个rune
为什么n==3,而n1==1呢?我们看下ReadRune 的源码
ReadBytes
ReadBytes方法,需要一个byte作为分隔符,读的时候从缓冲器里找出第一个出现的分隔符,缓冲器头部开始到分隔符之间的byte返回。
相当于有一个分隔符
ReadString
和readBytes方法类似
读入缓冲器
ReadFrom方法,从一个实现io.Reader接口的r,把r的内容读到缓冲器里,n返回读的数量
从缓冲器取出
Next方法,返回前n个byte(slice),原缓冲器变
缓冲区原理介绍
go字节缓冲区底层以字节切片做存储,切片存在长度len与容量cap, 缓冲区写从长度len的位置开始写,当len>cap时,会自动扩容。缓冲区读会从内置标记off位置开始读(off始终记录读的起始位置),当off==len时,表明缓冲区已全部读完
并重置缓冲区(len=off=0),此外当将要内容长度+已写的长度(即len) <= cap/2时,缓冲区前移覆盖掉已读的内容(off=0,len-=off),从避免缓冲区不断扩容
Golang bytes.Buffer - 传递值问题
【中文标题】Golang bytes.Buffer - 传递值问题【英文标题】:Golang bytes.Buffer - Passby value issue 【发布时间】:2018-12-27 09:11:14 【问题描述】:下面的 golang(go1.10.2) 代码会给出意外的输出
package main
import (
"bytes"
"fmt"
)
func main()
var b bytes.Buffer
//Commenting the below line will fix the problem
b.WriteString("aas-")
fmt.Printf("Before Calling - \"%s\"\n", b.String())
b = makeMeMad(b)
fmt.Printf("FinalValue - \"%s\"\n", b.String())
func makeMeMad(b bytes.Buffer) bytes.Buffer
b.WriteString("xcxxcx asdasdas dasdsd asdasdasdasd")
fmt.Printf("Write More - \"%s\"\n", b.String())
/*
//This will fix the problem
var newBuffer bytes.Buffer
newBuffer.WriteString(b.String())
return newBuffer
*/
return b
输出
Before Calling - "aas-"
Write More - "aas-xcxxcx asdasdas dasdsd asdasdasdasd"
FinalValue - "aas- "
我期待在输出的最后一行出现“aas-xcxxcx asdasdas dasdsd asdasdasdasd”。谁能解释一下。
【问题讨论】:
该问题已发布到 Go 的 github 上,您可以在这里观看:github.com/golang/go/issues/26462 所以你所经历的答案在这里:github.com/golang/go/issues/26462#issuecomment-406256821。bytes.Buffer
不应该一开始就按原样传递,总是传递一个指向它的指针。
【参考方案1】:
在后台bytes.Buffer
包含其他未导出的字段bootstrap
数组和buf
切片。虽然缓冲区内容是指向内部数组的小切片以避免分配。当您将 bytes.Buffer
参数作为值传递时,函数会收到一份副本。切片是引用类型,所以这个副本的切片继续指向原始缓冲区的数组。当您写入此副本的切片时,您实际上写入了原始的引导数组,副本的数组保持不变(在我们的例子中为“aas-”)。然后你返回这个副本,你可以打印它。但是当你将它分配给包含原始的变量时,引导数组首先被分配(“aas-”)然后buf
slice 指向它。
引导数组是 [64] 字节,因此您可以在代码 sn-p 和 see 中使用 >64 的长字符串文字,当缓冲区分配 buf 切片时,事情会按预期工作。
还有here
的简化示例试图说明为什么这一切看起来如此简洁。
【讨论】:
【参考方案2】:Golang FAQ 部分提到:
如果接口值包含指针*T,则方法调用可以获得 通过取消引用指针来获得一个值,但如果是一个接口值 包含一个值 T,方法调用没有有用的方法来获取 一个指针。
即使编译器可以将值的地址 传递给方法,如果方法修改了值,则更改将 迷失在来电者中。例如,如果 Write 方法的 bytes.Buffer 使用了值接收器而不是指针,这段代码:
var buf bytes.Buffer
io.Copy(buf, os.Stdin)
将标准输入复制到 buf 的副本中,而不是 buf 本身
错误是因为您没有在 makeMeMad
函数中传递缓冲区的地址。这就是为什么它没有覆盖 main 函数中的原始缓冲区。
将地址传递给创建的缓冲区以将字符串附加到现有缓冲区值。
package main
import (
"bytes"
"fmt"
)
func main()
var b bytes.Buffer
//Commenting the below line will fix the problem
b.WriteString("aas-")
fmt.Printf("Before Calling - \"%s\"\n", b.String())
makeMeMad(&b)
fmt.Printf("FinalValue - \"%s\"\n", b.String())
func makeMeMad(b *bytes.Buffer)
b.WriteString("xcxxcx asdasdas dasdsd asdasdasdasd")
fmt.Printf("Write More - \"%s\"\n", b.String())
/*
//This will fix the problem
var newBuffer bytes.Buffer
newBuffer.WriteString(b.String())
return newBuffer
*/
Playground Example
或者您可以将返回的缓冲区值分配给一个新变量,您将获得更新后的缓冲区值。
package main
import (
"bytes"
"fmt"
)
func main()
var b bytes.Buffer
//Commenting the below line will fix the problem
b.WriteString("aas-")
fmt.Printf("Before Calling - \"%s\"\n", b.String())
ab := makeMeMad(b)
fmt.Printf("FinalValue - \"%s\"\n", ab.String())
func makeMeMad(b bytes.Buffer) bytes.Buffer
b.WriteString("xcxxcx asdasdas dasdsd asdasdasdasd")
fmt.Printf("Write More - \"%s\"\n", b.String())
/*
//This will fix the problem
var newBuffer bytes.Buffer
newBuffer.WriteString(b.String())
return newBuffer
*/
return b
Go Playground 上的工作代码
或者您可以创建一个全局缓冲区,以便在任何函数写入缓冲区时更改缓冲区内的值。
package main
import (
"bytes"
"fmt"
)
var b bytes.Buffer
func main()
//Commenting the below line will fix the problem
b.WriteString("aas-")
fmt.Printf("Before Calling - \"%s\"\n", b.String())
b := makeMeMad(b)
fmt.Printf("FinalValue - \"%s\"\n", b.String())
func makeMeMad(b bytes.Buffer) bytes.Buffer
b.WriteString("xcxxcx asdasdas dasdsd asdasdasdasd")
fmt.Printf("Write More - \"%s\"\n", b.String())
/*
//This will fix the problem
var newBuffer bytes.Buffer
newBuffer.WriteString(b.String())
return newBuffer
*/
return b
Playground Example
【讨论】:
但他确实写了b = ...
对吗?所以值被重新写入b
,不是吗?
@leafbebop 检查编辑后的答案 OP 正在将缓冲区的副本传递给函数。
然后他返回一个修改过的缓冲区的副本,该缓冲区被重新分配给原始变量。
@leafbebop 是的,该值更新了缓冲区的状态,但返回时它丢弃了更改
@leafbebop 如果您仔细检查,您仍然没有写入主缓冲区,这并没有什么新鲜事。如果您真的想在缓冲区中添加一些内容,请使用 play.golang.org/p/ukqEyZaaCLI。以上是关于Golang bytes.buffer详解的主要内容,如果未能解决你的问题,请参考以下文章