存储一组序列化的Protobuf对象

4
我正在对我的Protobuf对象使用SerializeToString,然后将字符串存储在数据库中。
但是,有时我有一个这样的对象数组。我想要存储整个序列化的数组,为此我需要一些分隔符字符串来分隔序列化的字符串。
根据我看到的文档,该字符串只是一个字节数组,因此我无法保证其内容。
在这里,最好的方法是什么?
我不知道数组的长度,因为在过程中可能会添加对象,并且我希望在整个过程中将其存储在数据库中。

1
在字符串流前面加上长度信息怎么样? - Arunmu
我可以在每个字符串前面加上sizeof(int)。听起来应该可以。如果可以的话,我会尝试并发布结果。 - Mugen
2个回答

3
假设你的protobuf message长这样:
message Object
{
  ... = 1;
  ... = 2;
  ... = 3;
}

然后在同一个文件中,引入另外一个message,其中包含这些Object的集合。

message Objects
{
  repeated Object array = 1;
}

因此,当您有许多元素时,可以简单地使用 对象 并在 对象 上使用 SerializeAsString()。这将节省您序列化单个 对象 并放置自己手动制作的分隔符的工作量。您可以使用单个 对象 的实例序列化所有的对象
通过这种方法,您还可以将所有解析和序列化工作委托给 Protobuf 库。我在我的项目中使用了这种方法,它的效果非常好。
此外,明智地使用对象 也可以避免创建额外的 对象 副本。您可以向其中添加项目并使用索引访问。Protobufs 的 repeated 字段符合 C++11 标准,因此您也可以使用迭代器或增强的 for 循环进行操作。
需要注意的是,在将 Objects::SerializeAsString() 的输出存储到文件中时,应该首先输入该字符串的长度,然后才是实际序列化的字符串。在读取时,您可以先读取长度,然后是总字节数。为了方便使用,我扩展了 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
3
实际上,由于protobuf格式的细节,如果连接两个Objects(如上所定义),它与将array重复字段连接到单个实例并对其进行序列化完全等效。因此,您无需反序列化即可追加数据! - Kenton Varda
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - iammilind
1
@Mugen,已更新。同时请注意(如operator<<所示),我们需要将编码后的protobuf字符串的长度加上1个字节作为长度。这个length + 1最终在从文件中读取时会被使用。稍后可以进行反序列化。 - iammilind
1
@iammilind:子消息的编码方式与bytes字段完全相同,其中字节是序列化的子消息。因此,您可以定义message ObjectsRaw { repeated bytes array = 1; }并使用序列化的Object填充array,而无需对每个对象进行解码。生成的消息将与Objects二进制兼容。 - Kenton Varda
显示剩余4条评论

1
如果您无法保证分隔符与序列化数据内容唯一,那么我建议在每个对象的开头添加一个大小项,指示接下来的字符串有多长。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接