【问题标题】:Storing a set of serialized protobuf objects存储一组序列化的 protobuf 对象
【发布时间】:2016-07-12 05:57:03
【问题描述】:

我在我的 Protobuf 对象上使用SerializeToString,然后将字符串存储在数据库中。

但是,有时我有一个这样的对象数组。我想存储整个序列化数组,为此我需要在序列化字符串之间使用一些分隔符字符串。

根据我看到的documentation,字符串只是一个字节数组,所以我没有得到任何关于它的内容的承诺。

这里最好的方法是什么?

我不知道数组的长度,因为我们可能会在执行过程中将对象附加到其中,并且我希望在整个过程中将其存储在 DB 中。

【问题讨论】:

  • 在字符串流前面加上长度信息怎么样?
  • 我可以在每个字符串前面加上一个 sizeof(int) 前缀。听起来它应该工作。如果可行,我会尝试发布结果

标签: c++ serialization protocol-buffers separator


【解决方案1】:

假设,你的 protobuf message 看起来像这样:

message Object
{
  ... = 1;
  ... = 2;
  ... = 3;
}

然后在同一个文件中再引入 1 个message,这是这些Objects 的集合。

message Objects
{
  repeated Object array = 1;
}

因此,当您有许多元素时,您可以简单地使用Objects 并在Objects 本身上使用SerializeAsString()。这将节省您单独序列化单个Object 并放置您自己的手工分隔符的工作。您可以使用Objects 的单个实例序列化所有Objects。
使用这种方法,您将所有解析和序列化工作也委托给 Protobuf 库。我在我的项目中使用它,它就像一个魅力。

此外,明智地使用Objects 还可以避免额外复制Object。您可以向其中添加项目并使用索引进行访问。 protobufs 的 repeated 字段符合 C++11,因此您也可以将其与迭代器或增强的 for 循环一起使用。


需要注意的是,当您将Objects::SerializeAsString() 的输出存储到文件中时,您应该首先输入string 的长度,然后输入实际的序列化字符串。读取时,您可以先读取长度,然后再读取总字节数。为了方便使用,我扩展了std::fstream并重载了以下方法:

struct fstreamEncoded : std::fstream
{
    // other methods
    void  // returns `void` to avoid multiple `<<` in a single line
    operator<< (const string& content)
    {  // below casting would avoid recursive calling of this method
       // adding `length() + 1` to include the protobuf's last character as well
      static_cast<std::fstream&>(*this) << (content.length() + 1) << "\n" << content << std::endl;
    }

    string
    getline ()
    {
      char length_[20] = {};
      std::istream::getline(length_, sizeof(length_) - 1);
      if(*length_ == 0)
        return "";

      const size_t length = std::atol(length_);  // length of encoded input
      string content(length, 0);  // resize the `string`
      read(&content[0], length);  // member of `class istream`
      return content;
    }
}

以上只是一个插图。您可以根据自己的项目需求进行跟进。

【讨论】:

  • 虽然这是一个很好的解决方案,但在将序列化数据相互附加方面会很浪费。我将不得不反序列化、追加和重新序列化
  • @Mugen 其实由于protobuf格式的细节问题,如果你把Objects的两个实例(如上定义)拼接起来,完全等价于把array重复的字段拼接成一个单个实例并对其进行序列化。所以你不必反序列化来追加!
  • @KentonVarda,可能你的意思是Objects 的串联而不是Objects。是的,它是正确的。事实上,手动序列化每个Object,然后使用用户定义的分隔符将它们分离到一个文件中,这与以单个Objects 实例的形式序列化所有它们的内置protobuf 能力相比是相当麻烦的。不确定,OP 在哪里发现困难。
  • @Mugen,已更新。另请注意(如operator&lt;&lt; 所示),我们必须将编码的protobuf 字符串的length + 1 字节添加为长度。这个length + 1 最终将在从文件中读取时使用。稍后可以反序列化。
  • @iammilind:子消息的编码与 bytes 字段完全相同,其中字节是序列化的子消息。因此,您可以定义message ObjectsRaw { repeated bytes array = 1; } 并用序列化的Objects 填充array,而无需对每一个进行解码。生成的消息将与Objects 二进制兼容。
【解决方案2】:

如果你不能保证你的分隔符对于序列化数据的内容是唯一的,那么我会在每个对象的开头添加一个大小项,指示以下字符串的长度。

【讨论】:

    猜你喜欢
    • 2011-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    相关资源
    最近更新 更多