我还没有使用过encoding/gob 包(看起来很酷,我可能需要为它找到一个项目)。但是阅读 godoc,在我看来,每个编码都是一条记录,预计会从头到尾解码。也就是说,一旦你 Encode 一个流,生成的字节就是一个完整的集合,从头到尾尊重整个流 - 以后无法通过再次编码来附加。
godoc 声明编码的gob 是自我描述的。在编码流的开头,它描述了将跟随的整个数据集结构、类型等,包括字段名称。那么字节流后面就是那些Exported字段的值的大小和字节表示。
然后可以假设文档中省略的内容是因为流在一开始就自我描述了自己,包括即将传递的每个字段,这就是@ 987654324@会关心。 Decoder 不会知道在所描述的内容之后添加的任何连续字节,因为它只看到开头描述的内容。因此,该错误消息panic: extra data in buffer 是准确的。
在您的 Playground 示例中,您对同一个编码器实例进行了两次编码,然后关闭了文件。由于您要传入两条记录,并对两条记录进行编码,因此可能会起作用,因为编码器的单个实例可能会将两个 Encode 调用视为单个编码流。然后,当您关闭文件 io 的流时,gob 现在已完成 - 并且该流被视为单个记录(即使您以两种类型发送)。
在解码功能中也是如此,您从同一个流中读取 X 次。但是,您在关闭文件时正在编写一条记录 - 在该一条记录中实际上有两种类型。因此,为什么它在读取 2 时有效,并且正好是 2。但如果读取超过 2 则失败。
如果您想将其存储在单个文件中,一个解决方案是您需要为每个完整的“写入”或编码器实例/会话创建自己的索引。有些形成您自己的 Block 方法,允许您使用“开始”和“结束”标记包装或定义写入磁盘的每个条目。这样,在读回文件时,由于开始/结束标记,您确切知道要分配的缓冲区。一旦缓冲区中有一条记录,就可以使用 gob 的 Decoder 对其进行解码。并在每次写入后关闭文件。
我用于此类标记的模式类似于:
uint64:uint64
uint64:uint64
...
第一个是开始的字节数,第二个用冒号分隔的条目是它的长度。不过,我通常将其存储在另一个文件中,适当地称为indexes。这样它可以快速读入内存,然后我可以流式传输大文件,确切地知道每个开始和结束地址在字节流中的位置。
另一种选择是将每个gob 存储在自己的文件中,使用文件系统目录结构按照您认为合适的方式进行组织(或者甚至可以使用目录来定义类型)。那么每个文件的存在就是一条记录。这就是我使用事件溯源技术中渲染的 json 的方式,将数百万个文件存储在目录中。
总而言之,在我看来,gob 的数据是从头到尾的完整数据集 - 一个“记录”有你。如果您想存储多个编码/多个 gob,则需要创建自己的索引来跟踪每个 gob 字节的开始和大小/结束,因为您存储它们。然后,您将需要分别Decode 每个条目。