【问题标题】:Serialize Data Structures in C在 C 中序列化数据结构
【发布时间】:2010-09-27 03:33:20
【问题描述】:

我想要一个 C 库,它可以将我的数据结构序列化到磁盘,然后再加载它们。它应该接受任意嵌套的结构,可能带有循环引用。

我认为这个工具需要一个描述我的数据结构的配置文件。该库允许使用代码生成,尽管我相当确定没有它也可以做到这一点。

请注意,我对数据可移植性不感兴趣。我想把它用作缓存,这样我就可以依赖环境不变了。

谢谢。


结果

有人建议 Tpl 这是一个很棒的库,但我相信它不会做任意对象图,例如每个包含两个其他节点的节点树。

另一个候选人是Eet,它是启蒙窗口管理器的一个项目。看起来很有趣,但又似乎没有序列化嵌套结构的能力。

【问题讨论】:

    标签: c serialization


    【解决方案1】:

    查看tpl。从概述:

    Tpl 是一个用于序列化 C 的库 数据。数据存储在其 自然二进制形式。 API 很小 并试图保持“不碍事”。 与使用 XML 相比,tpl 更快 并且更易于在 C 程序中使用。 TPL 可以序列化许多 C 数据类型, 包括结构。

    【讨论】:

    • Tpl 似乎不支持嵌套结构。例如一个可以包含两个子节点的节点。
    • tpl 的另一个问题是浮动的可移植性有限。
    【解决方案2】:

    我假设您正在谈论存储图形结构,如果不是那么忽略...

    如果您存储一个图,我个人认为最好的办法是实现一个将您的图转换为邻接矩阵的函数。然后,您可以创建一个将邻接矩阵转换为图形数据结构的函数。

    这具有三个好处(在您的应用程序中可能很重要,也可能无关紧要):

    • 邻接矩阵是创建和存储图形的一种非常自然的方式
    • 您可以创建邻接矩阵并将其导入您的应用程序中
    • 您可以以有意义的方式存储和读取数据。

    我在 CS 项目中使用过这种方法,我肯定会再次这样做。

    您可以在此处阅读有关邻接矩阵的更多信息:http://en.wikipedia.org/wiki/Modified_adjacency_matrix

    【讨论】:

    • 我不是在谈论那种意义上的图结构。我的结构更像一棵树,尽管我不想排除循环引用。如果不代表我进行任何处理,C 库没有理由不能序列化它。
    • 树只是图的一种特殊情况,如果使用循环引用,人们可能会争辩说,事实上,你有一个图,而不是一棵树。
    • 很好:所有 C 数据结构都可以在内存图中查看。现在有可以序列化这些数据的库吗?
    【解决方案3】:

    我知道您要的是图书馆。如果你找不到(::boggle::,你会认为这是一个已解决的问题!),这里有一个解决方案的大纲:

    您应该能够编写代码生成器[1] 来序列化树/图,而无需(运行时)相当简单的预处理。

    您需要解析节点结构(typedef 处理?),并以直接的方式写入包含的数据值,但要小心处理指针。

    • 对于你知道被单独引用的其他对象(即char *name;)的指针,你可以直接序列化目标数据。

    • 对于可能被多次引用的对象和树的其他节点,您必须表示指针结构。每个对象都被分配了一个序列号,这是在指针的位置写出的。维护当前内存位置和序列号之间的转换结构。遇到指针时,查看它是否已经分配了一个数字,如果没有,则给它一个并将该对象排队等待序列化。

    回读还需要一个 node-#/memory-location 转换步骤,并且可能更容易在两遍中完成:使用指针槽中的节点号重新生成节点(错误指针,请注意)以找出位置每个节点都被放置,然后再次遍历结构来修复指针。

    我对 tpl 一无所知,但你也许可以利用它。


    磁盘/网络格式可能应该包含一些类型信息。您需要一个名称修饰方案。


    [1] ROOT 使用这种机制在 C++ 中提供非常灵活的序列化支持。


    后期添加:在我看来,这并不总是像我上面暗示的那么容易。考虑以下(人为设计且设计不当)声明:

    enum {
       mask_none = 0x00,
       mask_something = 0x01,
       mask_another = 0x02,
       /* ... */
       mask_all = 0xff
    };
    typedef struct mask_map {
       int mask_val;
       char *mask_name;
    } mask_map_t;
    mask_map_t mask_list[] = {
       {mask_something, "mask_something"},
       {mask_another, "mask_another"},
       /* ... */
    };
    struct saved_setup {
       char* name;
       /* various configuration data */
       char* mask_name;
       /* ... */
    };
    

    并假设我们初始化了struct saved_setup 项目,因此mask_name 指向mask_list[foo].mask_name

    我们去序列化数据的时候,struct saved_setup.mask_name怎么办?

    您需要小心设计数据结构和/或为序列化过程带来一些特定于案例的情报。

    【讨论】:

    • 感谢您的精彩文章。作为图书馆的“存在证明”,我基本上有这个计划。该理论是,可以用 C 语言想象的每个库都已编写完毕。不敢相信它竟然不存在。我可能会尝试在圣诞节写这篇文章。
    【解决方案4】:

    您可以查看eet。用于存储 C 数据类型(包括嵌套结构)的启蒙项目库。尽管启蒙项目的几乎所有库都处于 pre-alpha 状态,但 eet 已经发布。但是,我不确定它是否可以处理循环引用。应该不会吧。

    【讨论】:

      【解决方案5】:

      【讨论】:

      【解决方案6】:

      理论上YAML应该做你想做的http://code.google.com/p/yaml-cpp/

      如果它适合你,请告诉我。

      【讨论】:

      • 是的。然而,YAML C++ 将序列化 C 数据结构,尽管需要 C++ 编译器。其他 SO 读者可能会觉得这很有用。
      【解决方案7】:

      你应该检查 gwlib。串行器/解串器非常广泛。并且有大量的测试可供查看。 http://gwlib.com/

      【讨论】:

        【解决方案8】:

        另一个选项是Avro C,它是Apache Avro 在C 中的实现。

        【讨论】:

          【解决方案9】:

          这是我的解决方案。它使用我自己实现的 malloc、free 和 mmap、munmap 系统调用。按照给定的示例代码。参考:http://amscata.blogspot.com/2013/02/serialize-your-memory.html

          在我的方法中,我创建了一个 char 数组作为我自己的 RAM 空间。然后有分配内存和释放它们的函数。创建数据结构后,通过使用mmap,我将char数组写入文件。

          每当您想将其加载回内存时,都有一个函数使用munmap 将数据结构再次放入 char 数组。由于它具有指针的虚拟地址,因此您可以重新使用您的数据结构。这意味着,您可以创建数据结构、保存、加载、再次编辑和再次保存。

          【讨论】:

          • 即使对于嵌套的树结构,这也非常有效,尽管需要对代码进行一些更改才能使其编译。 'time.h' 缺少包含和 ushort 的假定 typedef,这不是该语言的标准。
          • 但是,它不处理指针数组,因此包含这些数组的图形结构没有被正确序列化,只有第一个指针被序列化。
          【解决方案10】:

          这是一个使用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", "Samsung Galaxy Charger");
            binn_object_set_double(obj, "price", 12.50);
            binn_object_set_blob(obj, "picture", picptr, piclen);
          
            // send over the network
            send(sock, binn_ptr(obj), binn_size(obj));
          
            // release the buffer
            binn_free(obj);
          

          如果你不想使用字符串作为键,你可以使用 binn_map,它使用整数作为键。

          还支持列表,所有这些结构都可以嵌套:

            binn *list;
          
            // create a new list
            list = binn_list();
          
            // add values to it
            binn_list_add_int32(list, 123);
            binn_list_add_double(list, 2.50);
          
            // add the list to the object
            binn_object_set_list(obj, "items", list);
          
            // or add the object to the list
            binn_list_add_object(list, obj);
          

          【讨论】:

            猜你喜欢
            • 2016-05-02
            • 1970-01-01
            • 1970-01-01
            • 2021-09-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-09-07
            • 1970-01-01
            相关资源
            最近更新 更多