我还没有使用过这个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 gob
of data 是从头到尾的完整数据集 - 单个“记录”有你。如果要存储多个编码/多个 gob,则需要创建自己的索引来跟踪gob
存储每个字节的开始和大小/结束。然后,您将需要Decode
分别输入每个条目。