使用Boost对复杂数据进行C ++序列化

时间:2009-02-16 21:32:21

标签: c++ serialization stl boost

我有一组我希望序列化数据的类。但是有很多数据,(我们正在讨论一个带有多达一百万或更多类实例的std :: map)。

不希望过早地优化我的代码,我想我会尝试一个简单而干净的XML实现,所以我使用tinyXML将数据保存到XML,但它太慢了。所以我开始考虑使用Boost.Serialization编写和读取标准ascii或二进制文件。

它似乎更适合于任务,因为在开始之前我不必将所有这些内存分配为开销。

我的问题基本上是如何为文件格式规划最佳序列化策略。如果不是必要的话,我并不特别希望序列化整个地图,因为它实际上只是我追求的内容。已经玩了一些序列化(并查看输出),我不明白如何加载数据可以知道它何时到达地图的末尾,例如,如果我只是一个接一个地保存所有项目。在规划序列化策略时,您需要考虑哪些问题?

感谢。

5 个答案:

答案 0 :(得分:4)

阅读此FAQ!这有助于开始吗?

答案 1 :(得分:3)

boost.serialization有许多优点。例如,正如您所说,只包含具有指定签名的方法,允许框架序列化和反序列化您的数据。此外,boost.serialization包括所有标准STL容器的序列化器和读取器,因此您不必担心所有键是否已存储(它们将)或如何在反序列化时检测映射中的最后一个条目(它将是自动检测。)

但是,有一些考虑因素需要考虑。例如,如果您的类中有一个字段用于计算或用于加速,例如索引或哈希表,则不必存储这些字段,您必须考虑到你必须从磁盘读取的数据重建这些结构。

至于你提到的“文件格式”,我想有时我们会尝试关注格式而不是数据。我的意思是,只要您能够使用(比如说)boost.serialization无缝地检索数据,文件的确切格式就无关紧要了。如果您想与不使用序列化的其他实用程序共享该文件,那就是另一回事。但仅仅出于(反)序列化的目的,您不必关心内部文件格式。

答案 2 :(得分:1)

如果没有必要,我并不特别希望序列化整个地图,因为它实际上只是我追求的内容。

这是否意味着你真的不需要序列化整个对象?也许你应该重新考虑使用基于文本的格式。如果您确实只需要在地图中序列化键/值对的子集,那么您应该将它们写入文本文件并稍后阅读。您不一定需要XML;每个地图键只有一行,后跟一行,值应该有效。

答案 3 :(得分:1)

如果您想要的只是键值对,那么重要的是键和值所采用的类型,这将为您处理事物的方式着色。

将地图本身序列化通常是一个糟糕的计划,因为您可能希望稍后更改关联容器类型,但不会使先前的序列化文件无效(或必须翻译)。

如果您希望避免再次重建容器的成本,那么序列化容器在某些情况下会很有用(但是预先调整容器大小通常足以避免绝大部分的开销)但这应该是基于决策的关于您的申请和使用的具体方面。

如果您提供键/值的类型,我们可以提供更多帮助。没有这个是一些一般的提示:

  • 如果他们能够接受字符串表示,那么一个简单的CSV文件就足够了(但是使用现有的读写器库,读取和写入合法的CSV比表面看起来更难)
  • 如果它们是固定宽度,那么简单的二进制格式将使阅读和书写非常容易(和快速),但应注意承认以下问题:
    • endianess
    • 是否希望允许简单地将这些文件放在一起,或者为了完整性添加类似CRC的值(你可以做到这两点但是更难)
    • 你失去了grep文件的能力(这是一个真正的损失,你可能最终不得不重新发明工具链的一部分)
    • 更改platform / compiler / size_t是否会破坏格式
  • 一些比XML轻的结构化文本格式。有几个JSOM / YAML等。这些将提供您很可能不需要的可扩展性。

答案 4 :(得分:1)

使用Google的Protocol Buffers,这是一种与语言无关,平台无关,可扩展的序列化结构化数据的方式,可用于通信协议,数据存储等。 Google对几乎所有内部​​RPC协议和文件格式都使用Protocol Buffers。

有C ++,Java,Python,Perl,C#和Ruby的绑定。

您在元数据.proto文件

中描述您的数据
message Person {
  required int32 id = 1;
  required string name = 2;
  optional string email = 3;
}

然后你会在C ++中使用它:

Person person;
person.set_id(123);
person.set_name("Bob");
person.set_email("bob@example.com");

fstream out("person.pb", ios::out | ios::binary | ios::trunc);
person.SerializeToOstream(&out);
out.close();

或者像这样:

Person person;
fstream in("person.pb", ios::in | ios::binary);
if (!person.ParseFromIstream(&in)) {
  cerr << "Failed to parse person.pb." << endl;
  exit(1);
}

cout << "ID: " << person.id() << endl;
cout << "name: " << person.name() << endl;
if (person.has_email()) {
  cout << "e-mail: " << person.email() << endl;
}

有关更完整的示例,请参阅tutorials