【问题标题】:Binary serialization in pure C/C++纯 C/C++ 中的二进制序列化
【发布时间】:2012-06-15 17:22:42
【问题描述】:

我想自己实现二进制序列化,不使用Boost 或任何其他第三方库。

在 C++ 中,实现它的最简单方法是使用ofstream,然后通过网络发送二进制文件。但是是否有任何其他流类可以用作临时缓冲区以避免将文件写入磁盘?

另外,我怎样才能在纯 C 中实现这一点?

【问题讨论】:

  • 您在寻找std::stringstream吗?
  • std::ostream 可以用作缓冲区,尽管二进制序列化通常意味着将对象和状态保存到磁盘。
  • 您声明 the aim is "to understand the process fully and not just calling/using some ready stuff" 并且您也知道 boost 可以满足您的需求......您为什么不简单地回顾一下 boost 是如何工作的,以便您了解它是如何工作的?
  • 谷歌的“写”和“读”。
  • @mah 因为 boost design 不是为教育目的而开发的,但是为了使用它,序列化的块不够清晰。

标签: c++ c serialization binary implementation


【解决方案1】:

在纯 C 中,您可以使用 Binn 格式。

示例代码:

  binn *obj;

  // create a new object
  obj = binn_object();

  // add values to it
  binn_object_set_int32(obj, "id", 123);
  binn_object_set_str(obj, "name", "John");
  binn_object_set_double(obj, "total", 2.55);

  // send over the network or save to a file...
  send(sock, binn_ptr(obj), binn_size(obj));

  // release the buffer
  binn_free(obj);

免责声明:我是创造者

【讨论】:

  • 他特别要求“不使用 Boost 或任何其他第三方库”。
【解决方案2】:

在某些情况下,在处理简单类型时,您可以这样做:

object o; 
socket.write(&o, sizeof(o)); 

【讨论】:

  • 由于人们对此表示反对,但没有发表评论,我将解释问题所在:由于结构的对齐问题,大多数编译器填充结构的成员变量以将它们对齐到某些字节的倍数.请参阅:stackoverflow.com/questions/119123/… 因此,如果使用由一个编译器为一个平台编译的代码编写此代码,并由另一个编译器编译的代码读取,或者为另一个平台使用相同的编译器,则此代码可能无法正常工作。代码是跨平台的,但它产生的数据不是。
  • 这会导致在使用方法略有不同的编译器时数据对齐不正确。此外,在某些情况下,数据将无法跨不同的架构、编译器甚至操作系统移植。 -1
  • 尽管我不鼓励使用 pragma,但我相信“pragma pack”在很大程度上解决了这个问题。我相信 GCC、clang、ICC 和 MSVC 都支持它,所以它可能是合理的。我提出它的原因是因为阅读并理解它是什么以及为什么需要它是一件好事。此外,由于这个答案没有向我们展示结构以及它是否使用 pragma pack,我们无法确定这是否有效。这段代码可能有效。
【解决方案3】:

我一直在使用 JSON 来序列化数据。这很简单,这是一件非常好的事情。正确处理 JSON 很容易,并且很容易判断它是否有任何问题。

它不像其他格式那样节省空间,但对于许多用途来说已经足够了。您可以从 JSON 网站获得免费的库代码。

http://json.org/

【讨论】:

    【解决方案4】:

    坚持是个难题。甚至将对象序列化到磁盘也并非易事。假设,例如,您在 C 中有这样的结构:

    struct Person {
        char name[100];
        int year;
    };
    

    这是一个 sef 包含的结构,可能是真正应用序列化的最简单方法。但是,您将不得不面对以下问题:

    1. 编译器的填充系统。在内存中完成一个结构,使其占用内存中的整数个字的方法是不标准的。

    2. 操作系统和机器本身以二进制形式表示数据的方式。显然,这种表示从一台机器变为另一台机器。

    结论是,即使是由同一操作系统中的同一程序创建的文件,也可能与同一操作系统中的同一程序不兼容,因为可能两个程序是用不同的 C 编译器编译的。

    现在让我们看看 C++ 中的一个对象:

    class Person {
    public:
        // more things...
    
    private:
        string name;
        Date * birth;
        Firm * firm;
    };
    

    现在同样的事情变得非常复杂。对象不再是自包含的,你应该按照指针来决定如何处理每个对象(这被称为 3. 指针混合和传递持久性)。你仍然有 1) 和 2) 的问题。

    假设您专注于自包含对象,但仍然需要针对第 1 点和第 2 点的解决方案。唯一的方法是确定 a) 文本格式或 b) 字节码格式的表示。字节码格式可以被任何操作系统中的任何程序理解,用任何 C 编译器编译,因为信息是逐字节读取和写入的。这是 Java 或 C# 序列化其对象的方式。作为表示的文本格式与字节码一样有效,但速度较慢。它的主要优点是它可以被人和计算机理解(结构化文本格式可以是 XML)。

    因此,为了序列化您的自包含对象,无论选择何种输出格式,您都需要具有能够读取整数、字符、字符串等的基本函数(或 C++ 中的类)。当您拥有每个对象的写/读对时,您必须为程序员提供为她的对象创建自己的写/读对的可能性,使用您的读/写对来处理基本数据。

    我们在这里讨论的是一个完整的框架,类似于 Python 提供的 pickle 模块。

    最后,能够缓存您的序列化而不是将其保存到磁盘这一事实是您遇到的最少的问题。如果您使用基于文本的格式,则可以使用 ostringstream 类,如果使用字节码,则可以使用内存块。

    如您所见,这不是一项简单的工作。 希望这会有所帮助。

    【讨论】:

    • 非常感谢这么好的 5 星回答,我问这样的问题是因为,我需要序列化结构,它用作服务器的 SSL 消息。我正在尝试在没有 OpenSSL 的情况下手动使用 SSL,并且对自二进制序列化也很感兴趣!这就是为什么我问这样的问题。再次感谢!
    猜你喜欢
    • 2011-06-08
    • 1970-01-01
    • 2016-07-27
    • 2017-10-18
    • 1970-01-01
    • 2010-12-17
    • 2015-09-17
    • 2016-02-21
    • 1970-01-01
    相关资源
    最近更新 更多