使用bytes.Buffer和*bytes.NewBuffer进行json.Unmarshal时的区别

6

我正在查看bytes包。如果我使用bytes.Buffer定义一个缓冲区,那么下面的代码可以工作并输出结果。但是,如果我尝试创建一个具有一定容量的缓冲区,然后再尝试相同的代码,它会出现错误:error: invalid character '\x00' looking for beginning of value。不确定如何修复它。

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
)

func main() {
    var jsonBlob = []byte(`[
        {"Name": "Platypus", "Order": "Monotremata"},
            {"Name": "Quoll",    "Order": "Dasyuromorphia"}
            ]`)

    //var b bytes.Buffer
    b := *bytes.NewBuffer(make([]byte, 20))
    b.Write(jsonBlob)

    fmt.Println(b.String())

    var dat interface{}
    err := json.Unmarshal(b.Bytes(), &dat)
    if err != nil {
        fmt.Println("error:", err)
    }   
    fmt.Printf("%+v", dat)
}

使用 bytes.Buffer 运行的输出

[
        {"Name": "Platypus", "Order": "Monotremata"},
        {"Name": "Quoll",    "Order": "Dasyuromorphia"}
    ]
[map[Name:Platypus Order:Monotremata] map[Name:Quoll Order:Dasyuromorphia]]
Program exited.

使用bytes.NewBuffer运行的输出

[
        {"Name": "Platypus", "Order": "Monotremata"},
        {"Name": "Quoll",    "Order": "Dasyuromorphia"}
    ]
error: invalid character '\x00' looking for beginning of value
<nil>
2个回答

11

NewBuffer函数使用参数作为缓冲区的初始内容。调用make([]byte, 20)返回一个包含20个零字节的字节切片。经过b.Write(jsonBlob)后,缓冲区的内容是20个零字节,后跟JSON文本。

fmt.Printf("%q\n", b.String())添加到程序中以查看缓冲区的内容。

带有printf的playground示例

JSON解析器抱怨第一个零字节。

如果您的目标是设置内部缓冲区的大小,请使用此代码:

b := bytes.NewBuffer(make([]byte, 0, 20))

make([]byte, 0, 20)调用返回一个长度为零,但容量为20的切片。

零长度切片的示例

类型为byte.Buffer的变量起始为空缓冲区。

如果您的目标是限制读取数据的数量,则使用io.LimitedReader。例如:

f, err := os.Open("filename")
if err != nil {
   // handle error
}
defer f.Close()
err := json.NewDecoder(&io.LimitedReader{N: 20, R: f}).Decode(&dat)
if err != nil {
    // handle error. Will get parse error if file is truncated. 
}

谢谢!有没有办法限制缓冲区不要增长?尽管将容量设置为20,但当大小增加时,缓冲区大小会增长直到溢出。我是否需要编写自己的Write方法来实现这一点?或者有没有一种方法可以在不编写自己的自定义包装器(例如eLitmus博客中的那种)的情况下实现这一点。 (参考 https://www.elitmus.com/blog/technology/custom-capacity-buffers-in-go/) - kbinstance
无法限制bytes.Buffer的大小。您能否在更高层次上描述一下您想要实现的目标? - Charlie Tumahai
我正在尝试将文件内容读入缓冲区,然后解析其内容。但是,如果由于文件大小过大而导致内存已满,则我的应用程序将崩溃。因此,我希望预先限制缓冲区大小并对其进行限制。这样当它达到上限时,我就可以报错了。 - kbinstance
请按照更新后的答案中所述,使用 io.LimitedReader - Charlie Tumahai

3

阅读这篇文章:

func NewBuffer

func NewBuffer(buf []byte) *Buffer NewBuffer函数创建并初始化了一个新的缓冲区,使用buf作为其初始内容。它旨在准备一个缓冲区以读取现有数据。它也可以用于调整写入时内部缓冲区的大小。为此,buf应该具有所需的容量,但长度为零。

在大多数情况下,只需声明一个Buffer变量或使用new(Buffer)就足以初始化一个缓冲区。

在执行b.Write(jsonBlob)后,由于您的缓冲区没有长度为零(make([]byte, 20)会创建一个长度为20的切片),因此b.Bytes()返回的是您分配的20个字节加上json内容。然后当您执行Unmarshal时,JSON解析器将在开头看到20个零,这显然会报错。


1
所以b.Bytes()只有20个字节,除非它不是。https://play.golang.org/p/plSPmgWDVE - zerkms
@zerkms 我的错误。已编辑。 - jfly

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接