【问题标题】:protocol buffers: how to serialize and deserialize multiple messages into a file (c++)?协议缓冲区:如何将多条消息序列化和反序列化到一个文件中(c++)?
【发布时间】:2014-05-17 21:26:15
【问题描述】:

我是 Protocol Buffers 和 c++ 的新手,但我的任务要求我使用这两者。我想多次将数据结构(消息)写入单个文件并能够读取数据。我可以读取和写入一条消息,但事实证明多条消息更难。我已经寻找了几个小时的答案,但我似乎无法将数据作为结构读取。任何示例代码或指针都会很有帮助。

这是我的结构的格式:

    typedef struct Entry
       {
          char  name[ NAME_MAX];
          int   id;
          int   age;
          char  DoB[32]; 
       } entry;

这是我一直用来写入文件的内容:

    Person_File file;
    fstream output("file", ios::out | ios::trunc | ios::binary);
    file.SerializeToOstream(&output);

我尝试将文件编辑选项更改为追加而不是截断,但这不允许我以我想要的格式读取数据。

这是我用来阅读的:

    fstream input("file", ios::in | ios::binary);
    file.ParseFromIstream(&input);

这些是我的 .proto 文件的内容:

    message Person {


message File {
    required string pname =1;
    required int32 id =2;
    required int32 age =3;
    required string DoB =4;
}

repeated File file =1;

}

从我所做的所有搜索来看,CodedInputStream/CodedOutputStream 似乎是我最好的选择,但我无法找到任何详细的示例或解释来帮助我理解。我知道协议缓冲区不是自定界的,这可能是我无法以原始格式读回消息的原因。 任何帮助,将不胜感激。谢谢

编辑: 我尝试根据收到的消息使用 CodedOutputStream,但它似乎没有将任何内容写入文件。

    int fd = open("file.txt",O_WRONLY | O_APPEND | O_CREAT);

    FileOutputStream* file_ostream_ = new FileOutputStream(fd);
    CodedOutputStream* ostream_ = new CodedOutputStream(file_ostream_);


ostream_->WriteLittleEndian32(file.ByteSize());
    file.SerializeToCodedStream(ostream_);

使用此代码后,文件变为空白。我哪里错了?

【问题讨论】:

标签: c++ structure protocol-buffers


【解决方案1】:

我尝试根据收到的消息使用 CodedOutputStream,但它 似乎没有向文件中写入任何内容。

我怀疑您的问题是您没有删除CodedOutputStreamFileOutputStream。这些对象缓冲输出并在其析构函数中刷新缓冲区,因此如果您从不销毁它们,它们将不会写入最后一个缓冲区,在这种情况下,它是唯一的缓冲区。

我建议在堆栈上分配这些对象(作为局部变量)。那么你就不可能忘记摧毁它们。

除此之外,这里的代码使用CodedInputStreamCodedOutputStream 来读取/写入分隔消息。每条消息都以表示大小的 varint 为前缀,其格式与 Java Protobuf 库的 writeDelimitedTo()/parseDelimitedFrom() 相同(C++ 库中从未出现过的功能)。

bool writeDelimitedTo(
    const google::protobuf::MessageLite& message,
    google::protobuf::io::ZeroCopyOutputStream* rawOutput) {
  google::protobuf::io::CodedOutputStream output(rawOutput);

  // Write the size.
  const int size = message.ByteSize();
  output.WriteVarint32(size);

  uint8_t* buffer = output.GetDirectBufferForNBytesAndAdvance(size);
  if (buffer != NULL) {
    // Optimization:  The message fits in one buffer, so use the faster
    // direct-to-array serialization path.
    message.SerializeWithCachedSizesToArray(buffer);
  } else {
    // Slightly-slower path when the message is multiple buffers.
    message.SerializeWithCachedSizes(&output);
    if (output.HadError()) return false;
  }

  return true;
}

bool readDelimitedFrom(
    google::protobuf::io::ZeroCopyInputStream* rawInput,
    google::protobuf::MessageLite* message) {
  google::protobuf::io::CodedInputStream input(rawInput);

  // Read the size.
  uint32_t size;
  if (!input.ReadVarint32(&size)) return false;

  // Tell the stream not to read beyond that size.
  auto limit = input.PushLimit(size);

  // Parse the message.
  if (!message->MergePartialFromCodedStream(&input)) return false;
  if (!input.ConsumedEntireMessage()) return false;

  // Release the limit.
  input.PopLimit(limit);

  return true;
}

【讨论】:

  • 宾果游戏!!!。删除 CodedOutputStream 和 FileOutputStream 就可以了。感谢您的出色回答
  • 我假设rawOutput 必须初始化为大小message.ByteSize() + sizeof(size),对吧?
  • @kakyo 大多数流类型不需要预先指定大小。如果您专门序列化为字节数组,那么我不建议使用分隔格式——这种格式的重点是支持将多条消息写入一个流。但是直接回答你的问题,不,message.ByteSize() + sizeof(size) 是不正确的。你需要message.ByteSize() + google::protobuf::io::CodedOutputStream::computeRawVarint32Size(size)
猜你喜欢
  • 2017-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-26
  • 1970-01-01
相关资源
最近更新 更多