File 通过多次追加来检索写入文件的GOB

File 通过多次追加来检索写入文件的GOB,file,go,append,gob,File,Go,Append,Gob,我正在尝试使用encoding/gob将数据存储到文件中,稍后再加载。我希望能够将新数据附加到文件中,并在以后加载所有保存的数据,例如重新启动应用程序后。使用Encode()存储到文件时没有问题,但在读取时,似乎总是只获取第一次存储的项,而不是简洁存储的项 下面是一个简单的例子: 如您所见,它可以向编码器写入两次,然后将其读回。但是,当关闭文件并以追加模式再次打开它时,写入似乎有效,但读取仅适用于前两个元素(之前已写入)。无法检索两个新添加的结构,我得到错误: 恐慌:缓冲区中有额外的数据 我知道

我正在尝试使用encoding/gob将数据存储到文件中,稍后再加载。我希望能够将新数据附加到文件中,并在以后加载所有保存的数据,例如重新启动应用程序后。使用Encode()存储到文件时没有问题,但在读取时,似乎总是只获取第一次存储的项,而不是简洁存储的项

下面是一个简单的例子:

如您所见,它可以向编码器写入两次,然后将其读回。但是,当关闭文件并以追加模式再次打开它时,写入似乎有效,但读取仅适用于前两个元素(之前已写入)。无法检索两个新添加的结构,我得到错误:

恐慌:缓冲区中有额外的数据

我知道,我也阅读


最后,我还发现,这似乎证明了我试图做的事情是行不通的。为什么?这个错误是什么意思?

我还没有使用
编码/gob
包(看起来很酷,我可能需要为它找到一个项目)。但是阅读godoc,在我看来,每个编码都是一个单独的记录,需要从头到尾进行解码。也就是说,一旦对一个流进行
编码
后,生成的字节是一个完整的集合,从开始到结束都与整个流有关,以后不能通过再次编码来追加

godoc声明编码的
gob
是自描述性的。在编码流的开头,它描述了后面的整个数据集结构、类型等,包括字段名。然后,字节流中接下来是这些导出字段的值的大小和字节表示

然后我们可以假设文档中省略的内容是因为流在一开始就自我描述,包括将要传递的每个字段,这就是
解码器所关心的。
解码器
将不知道在所描述的内容之后添加的任何连续字节,因为它只看到开头所描述的内容。因此,该错误消息
panic:extra data in buffer
是准确的

在游乐场示例中,将对同一编码器实例编码两次,然后关闭文件。由于您正好传入了两条记录,并对两条记录进行了编码,因此这可能会起作用,因为编码器的单个实例可能会将两个
Encode
调用视为单个编码流。然后,当您关闭文件io的流时,
gob
现在就完成了,并且流被视为一条记录(即使您发送了两种类型)

同样在解码函数中,您从同一个流中读取X次。但是,在关闭文件时,您正在编写一条记录,这条记录中实际上有两种类型。因此,为什么它在阅读2时起作用,确切地说是2。但如果读取超过2则失败

如果您想将其存储在单个文件中,一个解决方案是您需要为每个完整的“写入”或编码器实例/会话创建自己的索引。有些形成您自己的块方法,允许您使用“开始”和“结束”标记包装或定义写入磁盘的每个条目。这样,当读回文件时,由于开始/结束标记,您确切地知道要分配什么缓冲区。一旦缓冲区中有一条记录,就可以使用gob的
解码器
对其进行解码。并在每次写入后关闭该文件

我用于此类标记的模式类似于:

uint64:uint64
uint64:uint64
...
第一个是起始字节数,第二个条目由冒号分隔,其长度为。不过,我通常将其存储在另一个文件中,称为适当的
索引
。这样可以快速地将其读入内存,然后我就可以准确地知道每个起始地址和结束地址在字节流中的位置,对大文件进行流式处理

另一种选择是只将每个
gob
存储在自己的文件中,使用文件系统目录结构按您认为合适的方式组织(例如,甚至可以使用目录定义类型)。那么每个文件的存在就是一条记录。这就是我如何使用事件源技术中呈现的json,存储目录中组织的数百万个文件


总之,在我看来,一个
gob
的数据从头到尾都是一组完整的数据——你只有一条“记录”。如果要存储多个编码/多个gob,则to需要创建自己的索引,以便在存储时跟踪每个
gob
字节的开始和大小/结束。然后,您需要分别对每个条目进行解码。

谢谢,这很有意义。另一个选项应该是在连续写入之间不关闭编码器。在应用程序关闭之前,此操作应一直有效。是的,不关闭将起作用。Gob的特殊之处在于,对于遇到的每个新类型,它都会写入一条描述记录来定义字段和类型。通过创建新的编码器,它失去了对已定义类型的跟踪,因此它在第二次写入时再次开始定义它们。我想,这会让解码器感到困惑。这几乎就像他们缺少了一个
Close
m方法来表示它已经完成了。有人可能会说Close是隐式的:当不再有对编码器的引用时。