1

我有大量类似的对象(大约数百 GB),我需要对其进行序列化并按顺序写入文件,然后以相同的顺序读取它。如何在golang的protobuf(gogo proto)中做到这一点?Gob 有一个可以写入 io.Writer 的编码器,但 protobuf 没有类似的东西。可能是 protobuf 不是为此目的的最佳选择?我需要良好的性能和低内存分配。

4

1 回答 1

2

如果您想将多条消息写入单个文件或流,则由您来跟踪一条消息的结束位置和下一条消息的开始位置。协议缓冲区有线格式不是自定界的,因此协议缓冲区解析器无法自行确定消息的结束位置。解决此问题的最简单方法是在编写消息本身之前写入每条消息的大小。当您读回消息时,您读取大小,然后将字节读入单独的缓冲区,然后从该缓冲区解析。

资源

1. 编写 Protobuf

将您的 protobuf 编组为 a并与您要写入的文件一起[]byte调用. 这会将 的长度写入之前写入本身。Writeio.Writermsgio.Writermsg

func Write(w io.Writer, msg []byte) error {
    buf := make([]byte, 4)
    binary.LittleEndian.PutInt32(buf, Uint32(len(msg)))

    if _, err := w.Write(buf); err != nil {
        return err
    }

    if _, err := w.Write(msg); err != nil {
        return err
    }
}

2. 阅读 Protobuf

当你想读出另一边的 protobufs 时,打开文件并将其作为io.Reader. 这会从文件中提取大小,然后将该数量的字节读入msg缓冲区并返回它。

func Read(r io.Reader) ([]byte, error) {
    buf := make([]byte, 4)
    if _, err := io.ReadFull(r, buf); err != nil {
        return nil, err
    }

    size := binary.LittleEndian.Uint32(buf)

    msg := make([]byte, size)
    if _, err := io.ReadFull(r, msg); err != nil {
        return nil, err
    }

    return msg, err
}

Go 中的*os.File类型同时满足io.Readerio.Writer接口,所以你不应该遇到任何问题。

感谢@Brits 指出这一点。

祝你好运!

于 2019-12-03T20:10:03.493 回答