如何在文件中存储结构以轻松重新加载?

时间:2010-12-21 20:31:58

标签: c++ file-io

我有两个需要存储在文件中的结构。我找不到一个很好的方法来将它们放入一个文件而不解析结构并将其写为文本文件,这远远低于我想要的效率。将大多数基元的结构体转换为文件的最简单方法是什么?结构在

之下
struct object
{
    unsigned short id;
    float x;
    float y;
    unsigned short type;
    unsigned char flags;
};

struct sector
{
    unsigned long id;
    unsigned long neighbors[4]; // North,East,South,West
    std::vector<object> objects;
};

我已经尝试了下面的代码将结构写入文件,但它不能很好地工作。执行后文件的大小只有4.7kb,而它应该比这大得多。

sector s;
object o;
s.id = 1;
s.neighbors = {2, 3, 4, 5};
o.id = 1; o.x = 2.0f; o.y = 3.0f; o.type = 4; o.flags = 6;
for(int i = 0; i < 65536; i++)
{
    s.objects.push_back(o);
    o.id += 1; o.x += 1.0f; o.y += 1.0f; o.type += 1; o.flags += 1;
}

ofstream os("world.dat", ios::binary|ios::out);
s.objects.resize(s.objects.size());
int size = s.objects.size() * sizeof(object);
os.write((char*)&size, sizeof(int));
os.write((char*)&s, size);
os.close();

有什么建议吗?

6 个答案:

答案 0 :(得分:4)

听起来您可以使用序列化库,例如boost::serialization。这支持开箱即用的STL容器,因此在示例中支持结构应该非常简单。

答案 1 :(得分:3)

您不能使用原始字节I / O编写包含向量的结构。 vector类是一个小型数据结构,它将数据存储在堆上的某个位置,而不是存储在struct中。这是因为结构不可能提前“知道”它将保留多少个对象。

你可以这样写出载体的内容:

os.write((const char*)&s.id, sizeof(s.id));
os.write((const char*)&s.neighbors, sizeof(s.neighbors));
os.write((const char*)&s.objects[0], size);

至于为什么文件最终只有4.7 kB,我不知道。打印size以查看正在写入的数据量。我在上述更改后检查了代码,并且可以正常工作。

此外,您不需要s.objects.resize(s.objects.size());。将矢量调整为自己的大小是多余的。

还要注意一个小怪癖。我的架构上的sizeof(object)== 16,这比上面的结构要求的要多(2 + 4 + 4 + 2 + 1 == 13)。编译器通常添加填充以确保大小为2 n 的基元与2 n - 字节边界对齐,这导致每个结构内部和向量中的结构之间出现洞。

这样做的结果是你正在向文件写入额外的字节,其中一些是随机噪声。在非调试版本中,这些漏洞永远不会被任何东西填充,因此它们只包含在为您的目的分配之前该内存位置的垃圾。因此,如果您运行程序两次,则两个文件可能不相同。

答案 2 :(得分:0)

我想我刚刚想出了一个解决方案。对象的数量是常量,因此不需要使用向量。我将vector<object> objects替换为object objects[256][256],使用我当前的代码正确地将其写入文件。我也放弃了跟踪一个扇区相对于另一个扇区的位置,通过id和邻近的id来支持x,y坐标系。应该更容易跟踪。我希望我知道为什么我总是过于复杂化然后简化它们。

struct object
{
    unsigned short id;
    float x;
    float y;
    unsigned short type;
    unsigned char flags;
};
struct sector
{
    unsigned long x;
    unsigned long y;
    object objects[256][256];
};
sector s;
object o;

s.x = 1; s.y = 1;
o.id = 1; o.x = 2.0f; o.y = 3.0f; o.type = 4; o.flags = 6;
for(int x = 0; x < 256; x++)
{
    for(int y = 0; y < 256; y++)
    {
        s.objects[x][y] = o;
        o.id += 1; o.x += 1.0f; o.y += 1.0f; o.type += 1; o.flags += 1;
    }
}

ofstream os("world.dat", ios::binary|ios::out);
int size = sizeof(sector);
os.write((char*)&s, size);
os.close();

这是正常的。我想我正在做的是建立一个2d地图,每个节点都包含自己的2d地图。也许有更好的方法。

答案 3 :(得分:0)

您是否看过fread()fwrite()?如果你不需要文件的人类可读性,而对象和扇区都是POD,那么这种事情对你有用:

object o;
FILE * fh = ...;
...
fwrite( &o, sizeof(o), 1, fh );

fread( &o, sizeof(o), 1, fh );

通常我不会推荐这种面向C语言的编码,但有时旧方法仍然是最简单的。

请注意,此代码要求object中的扇区数组具有恒定的大小。

答案 4 :(得分:0)

如果你想知道为什么只有4.7K存储在文件中,你需要说明你正在使用哪个操作系统以及哪个C ++编译器。因为's'在堆栈上,所以当你选择将'&amp; s'写入文件时,只有4.7K的堆栈被使用是合理的假设。在Windows上,环境块占用3.8K的堆栈空间。因此,在Windows上,您的应用程序在您选择调用'&amp; s'上的write()时使用额外的0.9K堆栈空间(在环境块之后),并且因为堆栈向下增长,所以C ++实现只选择(静默地)写入()直到堆栈开始的点,即“&amp; s”以上4.7K。

答案 5 :(得分:0)

如果您想避免使用库,那么您的结构应该是POD。然后,您可以将整个结构存储在二进制文件中。否则,您可能会在堆上分配数据。