1

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

这是一个最小的例子: https: //play.golang.org/p/patGkKDLhM

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

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

我知道在磁盘上的文件中附加到 golang gob并且我还阅读了https://groups.google.com/forum/#!topic/golang-nuts/bn6vjC5Abd8

最后,我还找到了https://gist.github.com/kjk/8015952,这似乎表明我正在尝试做的事情不起作用。为什么?这个错误是什么意思?

4

1 回答 1

3

我还没有使用过这个encoding/gob包(看起来很酷,我可能需要为它找到一个项目)。但是阅读 godoc,在我看来,每个编码都是一条记录,预计会从头到尾解码。也就是说,一旦您Encode成为流,生成的字节就是一个完整的集合,从头到尾尊重整个流 - 以后无法通过再次编码来附加。

godoc 声明编码gob是自描述的。在编码流的开头,它描述了将跟随的整个数据集结构、类型等,包括字段名称。然后字节流中的内容是那些导出字段的值的大小和字节表示。

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

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

在解码功能中也是如此,您正在从同一个流中读取 X 次。但是,您在关闭文件时正在编写一条记录 - 在该一条记录中实际上有两种类型。因此,为什么它在读取 2 和 EXACTLY 2 时有效。但如果读取超过 2 则失败。

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

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

uint64:uint64
uint64:uint64
...

第一个是开始的字节数,第二个由冒号分隔的条目是它的长度。不过,我通常将其存储在另一个文件中,名为indexes. 这样它可以快速读入内存,然后我可以流式传输大文件,确切地知道每个开始和结束地址在字节流中的位置。

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

总而言之,在我看来,a gobof data 是从头到尾的完整数据集 - 单个“记录”有你。如果要存储多个编码/多个 gob,则需要创建自己的索引来跟踪gob存储每个字节的开始和大小/结束。然后,您将需要Decode分别输入每个条目。

于 2016-04-03T14:43:22.853 回答