【问题标题】:C++ serialization of complex data using Boost使用 Boost 对复杂数据进行 C++ 序列化
【发布时间】:2010-10-07 23:11:42
【问题描述】:

我有一组想要序列化数据的类。但是有很多数据(我们说的是一个 std::map 具有多达一百万个或更多类实例)。

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

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

我的问题本质上是如何为文件格式规划最佳序列化策略。如果没有必要,我并不特别想序列化整个地图,因为它实际上只是我所追求的内容。玩了一下序列化(并查看了输出),我不明白如何将数据重新加载到地图的末尾,例如,如果我只是一个接一个地保存所有项目.在规划序列化策略时需要考虑哪些问题?

谢谢。

【问题讨论】:

    标签: c++ serialization stl boost


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:

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

      有针对 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

      【讨论】:

        【解决方案3】:

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

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

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

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

        • 如果它们适合字符串表示,那么一个简单的 CSV 文件可能就足够了(但使用现有的读写器库,读取和写入合法的 CSV 比表面上看起来更难)
        • 如果它们是固定宽度,那么简单的二进制格式将使读写变得非常容易(和快速),但应注意确认以下问题:
          • 字节序
          • 您是否希望允许将此类文件简单地组合在一起,或者添加类似 CRC 的值以实现完整性(您可以同时执行这两种操作,但更难)
          • 您失去了 grep 文件的能力(这是一个真正的损失,您可能不得不为此重新发明部分工具链)
          • 更改平台/编译器/size_t 是否会破坏格式
        • 一些比 XML 更轻量的结构化文本格式。有几种 JSOM/YAML 等。这些将提供您很可能不需要的可扩展性。

        【讨论】:

        • 抱歉,我错过了我原本打算完全离开 xml 的原始答案,因为我觉得标准的 ascii / 二进制就足够了(现在已经编辑了问题)。不过感谢您的意见,那里有一些有用的信息。
        【解决方案4】:

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

        这是否意味着您真的不需要序列化整个对象?也许您应该重新考虑仅使用基于文本的格式。如果您真的只需要序列化映射中键/值对的子集,那么您可能应该将它们写入文本文件并稍后读取它们。你不一定需要XML;每个映射键只有一行,然后是带有值的一行应该可以工作。

        【讨论】:

          【解决方案5】:

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-05-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多