为啥 io.reader 读完后会变空?

Posted

技术标签:

【中文标题】为啥 io.reader 读完后会变空?【英文标题】:Why does an io.reader become empty after reading it?为什么 io.reader 读完后会变空? 【发布时间】:2015-09-03 19:18:12 【问题描述】:
func foo(buf *bytes.Buffer) 
    fmt.Println("0: ", len(buf.Bytes()))
    ioutil.ReadAll(buf)
    fmt.Println("1: ", len(buf.Bytes()))

代码第一次显示正确的长度,但第二次显示长度为零。

【问题讨论】:

【参考方案1】:

bytes.Buffer 读取会耗尽或消耗已读取的字节。这意味着如果您再次尝试阅读,这些内容将不会被返回。

Buffer.Bytes() 返回缓冲区的 unread 部分,因此在读取所有内容后看到0 长度是预期的结果(这正是ioutil.ReadAll() 所做的) .


如果您只想“窥视”而不是真正“读取”字节怎么办?

bytes.Buffer 中没有“窥视”功能。最简单的方法是获取缓冲区的字节,并从中构造另一个 bytes.Buffer 并从新缓冲区中读取。

它可能看起来像这样:

func peek(buf *bytes.Buffer, b []byte) (int, error) 
    buf2 := bytes.NewBuffer(buf.Bytes())
    return buf2.Read(b)

peek() 在行动:

为简单起见省略了错误检查:

buf := &bytes.Buffer

buf.WriteString("Hello")
fmt.Printf("Len: %d, Content: %s\n", buf.Len(), buf)

fmt.Println("\nPeeking...")
data := make([]byte, 4)
peek(buf, data)
fmt.Printf("Peeked: %s\n", data)
fmt.Printf("Len: %d, Content: %s\n", buf.Len(), buf)

fmt.Println("\nReading...")
data = make([]byte, buf.Len())
buf.Read(data)
fmt.Printf("Read: %s\n", data)
fmt.Printf("Len: %d, Content: %s\n", buf.Len(), buf)

输出(在Go Playground上试试):

Len: 5, Content: Hello

Peeking...
Peeked: Hell
Len: 5, Content: Hello

Reading...
Read: Hello
Len: 0, Content: 

【讨论】:

非常感谢。我明白。是否有任何方法可以第二次阅读其内容? 这种方法不会复制任何东西(除了b中的数据),只是创建一个新的缓冲区。我可以看到这更快的唯一方法是执行“return copy(buf.Bytes(),b),nil”,它具有相同的效果但不分配新的缓冲区。不确定哪个实际上更快。 (猜测副本是) @DavidBudworth 是的,你是对的。 Buffer.Bytes() 只返回一个共享相同底层数组的切片。所以在性能方面一点也不差。 对于可能还没有Buffer.Bytes() 中的所有数据的网络缓冲区使用这种方法似乎存在问题。 @Xeoncross bytes.Buffer 是一个内存缓冲区,它与网络无关。在网络的情况下,我们必须负责“重放”读取和检查的数据。可以在此答案中找到一个示例:Golang read request body

以上是关于为啥 io.reader 读完后会变空?的主要内容,如果未能解决你的问题,请参考以下文章

在golang的io.read实现中状态是否被破坏?

漫画 | 硬盘装满电影后会变重吗?

Redmine自定义字段增多后会变慢

IO流19 - 字符流 -FileRead写文本

为啥codeigniter会变慢?

为啥在视图中使用此查询会变慢?