const n, m = 3, 2
buf := &bytes.Buffer{}
e := gob.NewEncoder(buf)
for i := 0; i < n; i++ {
he(e.Encode(&Point{X: i, Y: i * 2}))
}
e = gob.NewEncoder(buf)
for i := 0; i < m; i++ {
he(e.Encode(&Point{X: i, Y: 10 + i}))
}
d := gob.NewDecoder(buf)
for i := 0; i < n; i++ {
var p *Point
he(d.Decode(&p))
fmt.Println(p)
}
d = gob.NewDecoder(buf)
for i := 0; i < m; i++ {
var p *Point
he(d.Decode(&p))
fmt.Println(p)
}
3条答案
按热度按时间vjhs03f71#
gob
软件包并不是为这种方式设计的。一个gob流必须由一个gob.Encoder
写入,也必须由一个gob.Decoder
读取。这是因为
gob
包不仅序列化你传递给它的值,它还传输数据来描述它们的类型:一串垃圾是自我描述的。流中的每个数据项前面都有其类型的规范,以一小组预定义类型的形式表示。
这是编码器/解码器的状态-关于什么类型以及它们如何被传输,随后的新编码器/解码器将不会(不能)分析“先前的”流以重构相同的状态并且在先前的编码器/解码器停止的地方继续。
当然,如果创建一个
gob.Encoder
,可以使用它来序列化任意多的值。你也可以创建一个
gob.Encoder
并写入一个文件,然后再创建一个新的gob.Encoder
,并附加到同一个文件中,但是你必须使用2个gob.Decoder
来读取这些值,这与编码过程完全匹配。作为演示,让我们跟随一个例子。本示例将写入内存中的缓冲区(
bytes.Buffer
)。2个后续的编码器将写入它,然后我们将使用2个后续的解码器来读取值。我们将写这个结构的值:对于简短的代码,我使用这个“错误处理程序”函数:
现在代码:
输出(在Go Playground上尝试):
请注意,如果我们只使用一个解码器来读取所有值(循环直到
i < n + m
,当迭代到达n + 1
时,我们会得到与您在问题中发布的相同的错误消息,因为后续数据不是序列化的Point
,而是新gob
流的开始。因此,如果你想坚持使用
gob
包来做你想做的事情,你必须稍微修改,* 增强 * 你的编码/解码过程。当使用新的编码器时,你必须以某种方式标记边界(所以当解码时,你会知道你必须创建一个新的解码器来读取后续的值)。您可以使用不同的技术来实现这一点:
这里需要注意的一些事情:
gob
包是最高效、最 * 紧凑 * 的,因为每次创建和使用新编码器时,类型规范都必须重新传输,导致更多开销,并使编码/解码过程变慢。如果你想要搜索功能,你需要单独管理一个 index 文件,它会告诉你新的编码器/解码器从哪个位置开始,这样你就可以搜索到那个位置,创建一个新的解码器,并从那里开始阅读值。
警告
gob.NewDecoder()
文件说明:如果r没有实现io.ByteReader,它将被 Package 在bufio. Reader中。
这意味着,如果你使用
os.File
(它没有实现io.ByteReader
),内部使用的bufio.Reader
可能会从传递的读取器读取比gob.Decoder
实际使用的更多的数据(正如它的名字所说,它做缓冲IO)。因此,在同一个输入读取器上使用多个解码器可能会导致解码错误,因为前一个解码器的内部使用的bufio.Reader
可能会读取不会被使用的数据并传递给下一个解码器。对此的一个解决方案是显式传递一个实现
io.ByteReader
的读取器,该读取器不会“提前”读取缓冲区。例如:请看一个没有这个 Package 器的错误示例:https://go.dev/play/p/dp1a4dMDmNc
看看上面的wrapper是如何解决这个问题的:https://go.dev/play/p/iw528FTFxmU
查看相关问题:将结构体高效地序列化到磁盘
0s7z1bwu2#
除了上面的,我建议使用一个中间结构来排除gob头:
xvw2m8pv3#
除了伟大的icza答案,你可以使用以下技巧来附加到一个gob文件与已经写入的数据:当附加第一次写入丢弃第一编码时:
1.像往常一样创建文件Encode gob(首先对写头进行编码)
1.关闭文件
1.打开文件进行 append
1.使用和中间编写器编码伪结构(写入标头)
1.重置编写器
1.像往常一样对gob进行编码(不写入头)
示例: