我有重复的消息,我想存储在一个文件中。目前,我必须将这个重复的消息包装在另一个消息中。有办法解决这个问题吗?
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;
}
协议缓冲区文档中的"技术"部分对重复消息的描述如下:
如果你想写多条消息对于单个文件或流,它是up的给你留个踪迹在哪里消息结束,下一个开始。的协议缓冲区线格式不是自定界,所以协议缓冲区解析器无法确定消息自己结束。最简单的解决这个问题的方法是写你面前的每条信息的大小编写消息本身。当你把信息读回来,你读大小,然后将字节读入分离缓冲区,然后从中解析缓冲区。(如果你想避免复制字节到一个单独的缓冲区,签出CodedInputStream类(在两者中)c++和Java),它可以被告知将读取限制为一定数量的字节。)
在c++和Java中也有一种常规的实现方法。看看这个堆栈溢出线程的详细信息:在Java中是否有与协议缓冲区分隔的I/O函数等效的c++ ?
Protobuf不支持此功能。它可以用来序列化一条消息,但是这个序列化的消息不包含关于它的类型(Box或Boxes)和长度的信息。所以如果你想存储多条消息你还必须包括消息的类型和长度。编写算法(用伪语言)可以像这样:
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)
}
}
我正在研究这个问题,最后选择了Parquet。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();
它可能不适合您的用例,但我认为它值得在这里提及。
在java中可以使用分隔符消息。关于c++,请参阅在Java中是否有与协议缓冲区分隔的I/O函数等效的c++ ?
基本上在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你需要算出