在一个协议缓冲区二进制文件中存储多个消息

时间:2011-04-07 19:12:37

标签: c++ python protocol-buffers

我有重复的消息,我想存储在一个文件中。目前,我必须在另一条消息中包装此重复消息。有办法解决这个问题吗?

package foo;

message Box {
  required int32 tl_x = 1;
  required int32 tl_y = 2;
  required int32 w = 3;
  required int32 h = 4;
}

message Boxes {
  repeated Box boxes = 1;
}

4 个答案:

答案 0 :(得分:12)

以下是Protocol Buffers文档中有关重复消息的"Techniques"部分的内容:

  

如果要编写多条消息   单个文件或流,它已启动   告诉你一个人在哪里   消息结束,下一个消息开始。该   协议缓冲区的有线格式不是   自定界,所以协议缓冲区   解析器无法确定何处   消息自行结束。最简单的   解决这个问题的方法是写   你之前每封邮件的大小   写消息本身。当你   阅读回来的消息,你读   大小,然后将字节读入a   单独的缓冲区,然后从中解析   缓冲。 (如果你想避免复制   字节到单独的缓冲区,检查   CodedInputStream类(两者都有)   可以告诉的C ++和Java)   限制读取到一定数量   字节)。

还有一种在C ++和Java中实现它的传统方法。有关详细信息,请查看此Stack Overflow线程:Are there C++ equivalents for the Protocol Buffers delimited I/O functions in Java?

答案 1 :(得分:6)

Protobuf不支持此功能。它可用于序列化一条消息,但此序列化消息不包含有关其类型(Box或Box)和长度的信息。因此,如果您想存储多条消息,您还必须包含消息的类型和长度。编写算法(伪语言)可能如下所示:

for every message {
    write(type_of_message) // 1 byte long
    write(length_of_serialized_message) // 4 bytes long
    write(serialized_message)
}

加载算法:

while(end_of_file) {

    type = read(1) // 1 byte
    length = read(4) // 4 bytes
    buffer = read(length)
    switch (type) {
      case 1:
         deserialise_message_1(buffer)
      case 2:
         deserialise_message_2(buffer)
    }
}

答案 2 :(得分:1)

在java中,您可以使用分隔的消息。对于C ++,请参阅Are there C++ equivalents for the Protocol Buffers delimited I/O functions in Java?

基本上在C ++中根据上述

const unsigned bufLength = 256;
unsigned char buffer[bufLength];
Message protoMessage;

google::protobuf::io::ArrayOutputStream arrayOutput(buffer, bufLength);
google::protobuf::io::CodedOutputStream codedOutput(&arrayOutput);

codedOutput.WriteLittleEndian32(protoMessage.ByteSize());
protoMessage.SerializeToCodedStream(&codedOutput);

和python你需要解决它

答案 3 :(得分:0)

我只是在解决这个问题,最后选择了enter image description here。 Parquet非常适合在文件中存储一堆Protobuf消息,并使其在以后的工作中更加容易。

这段代码将创建Parquet文件:

Path path = new Path("/tmp/mydata.parq");
CompressionCodecName codecName = CompressionCodecName.SNAPPY;
int blockSize = 134217728;
int pageSize = 1048576;
boolean enableDictionary = true;
boolean validating = false;

ProtoParquetWriter<Message> writer
    = new ProtoParquetWriter<>(
        path,
        Box.class,
        codecName,
        blockSize,
        pageSize,
        enableDictionary,
        validating
    );

for (Message message : messages) {
    writer.write(message);
}

writer.close();

它可能不适合您的用例,但我认为在这里值得一提。