C ++将对象存储在文件中

时间:2009-09-28 18:42:23

标签: c++ object binary storage fstream

我有一个对象列表,我希望将其存储在尽可能小的文件中以便以后检索。我一直在仔细阅读this tutorial,并开始(我认为)理解,但有几个问题。以下是我正在使用的代码段:

    static bool writeHistory(string fileName)
{
    fstream historyFile;
    historyFile.open(fileName.c_str(), ios::binary);
    if (historyFile.good())
    {
        list<Referral>::iterator i;
        for(i = AllReferrals.begin(); 
                i != AllReferrals.end();
                i++)
        {
            historyFile.write((char*)&(*i),sizeof(Referral));
        }
        return true;
    } else return false;
}

现在,这是从片段

改编的
file.write((char*)&object,sizeof(className));

摘自教程。现在我相信它正在做的是将对象转换为指针,获取值和大小并将其写入文件。但如果这样做,为什么还要费心去做转换呢?为什么不从头开始接受价值?为什么它需要大小?此外,根据我的理解,为什么

historyFile.write((char*)i,sizeof(Referral));

不编译?我是一个迭代器(并不是一个迭代器指针?)。或者只是

historyFile.write(i,sizeof(Referral));

为什么我还需要搞乱地址呢?我不是将数据存储在文件中吗?如果地址/值是自己持久的,为什么我不能只存储以纯文本格式化的地址而不是稍后获取它们的值?

我还应该使用.txt扩展程序吗?的&LT;编辑&gt; 然后我应该使用什么?我试过.dtb并且无法创建文件。的&LT; / edit&gt; 我实际上甚至无法使用ios :: binary标志在没有错误的情况下打开文件。我也无法传递文件名(作为字符串类字符串,由c_str()转换回来,它编译但是会出错。)

很抱歉这么多小问题,但这基本上总结为如何有效地将对象存储在文件中?

6 个答案:

答案 0 :(得分:7)

您要做的是称为序列化。为此,Boost有very good library

在某些情况下,您尝试做的事情可能会有一些非常重要的条件。它仅适用于POD种类型。它只能保证适用于使用相同版本的编译器编译的代码,并且具有相同的参数。

(char*)&(*i)

说要取迭代器i,取消引用它来获取你的对象,获取它的地址并将其视为一个字符数组。这是写入文件的开始。 sizeof(Referral)是要写出的字节数。

否,迭代器不一定是指针,尽管指针满足迭代器的所有要求。

答案 1 :(得分:2)

问题#1为什么......不编译? 答:因为我不是推荐* - 它是一个列表:: iterator ;;迭代器是指针上的抽象,但它不是指针。

问题#2我还应该使用.txt扩展名吗? 答:可能不是。 .txt由许多系统关联到MIME类型text / plain。

未提出问题:这有效吗? 答:如果推荐有任何指示,。当您尝试从文件中读取引荐时,指针将指向内存中使用 的内容的位置,但是那里不能保证任何在那里有效,至少是指针最初指向的东西。小心。

答案 2 :(得分:2)

  

不是指针的迭代器吗?

迭代器就像一个来自外部的指针。在大多数(可能是所有)情况下,它实际上是某种形式的对象而不是裸指针。迭代器可能包含一个指针作为它用来执行其作业的内部成员变量,但它也可能包含其他内容或必要时的其他变量。

此外,即使迭代器内部有一个简单的指针,也可能不会直接指向您感兴趣的对象。它可能指向容器类使用的某种簿记组件,然后它可以使用获得感兴趣的实际对象。幸运的是,我们不需要关心那些内部细节究竟是什么。

因此,考虑到这一点,这是(char*)&(*i)中发生的事情。

  • *i返回对列表中存储的对象的引用。
  • &获取该对象的地址,从而产生指向该对象的指针。
  • (char*)将该对象指针强制转换为char指针。

这段代码将是做这样的事情的简短形式:

Referral& r = *i;
Referral* pr = &r;
char* pc = (char*)pr;

  

为什么我需要搞乱   还是带地址?

     

为什么它需要大小?

fstream::write旨在将一系列字节写入文件。它对这些字节的含义一无所知。你给它一个地址,以便它可以写入从该地址指向的任何地方开始存在的字节。你给它一个大小,以便它知道要写入多少字节。

所以,如果我这样做:

MyClass ExampleObject;
file.write((char*)ExampleObject, sizeof(ExampleObject));

然后它将ExampleObject内直接存在的所有字节写入文件。

注意:正如其他人所提到的,如果您要编写的对象具有动态分配内存或以其他方式使用指针的成员,那么指向的内存将不会由单个简单的fstream::write调用写入。


  

序列化会显着提高存储效率吗?

理论上,二进制数据通常既可以比纯文本小,也可以更快地读写。在实践中,除非您处理大量数据,否则您可能永远不会注意到差异。硬盘很大,处理器现在很快。

效率不是唯一要考虑的因素:

  • 如有必要,二进制数据更难以检查,调试和修改。至少没有其他工具,但即使这样,纯文本仍然通常更容易。
  • 如果您的数据文件将在程序的不同版本之间保留,那么如果您需要更改对象的布局会发生什么?编写代码可能会令人恼火,因此版本2程序可以读取版本1文件中的对象。此外,除非您提前采取行动(例如将版本号写入文件),否则读取版本2文件的版本1程序可能会出现严重问题。
  • 您是否需要验证数据?例如,反腐败或恶意更改。在像这样的二进制方案中,您需要编写额外的代码。而当使用纯文本时,转换例程通常可以帮助填补验证卷。

当然,一个好的序列化库可以帮助解决其中的一些问题。一个好的纯文本格式库(例如,XML的库)也是如此。如果您还在学习,那么我建议您尝试两种方式来了解它们的工作方式以及最适合您的目的。

答案 3 :(得分:1)

您要做的事情(向/从文件读取和写入原始内存)将调用未定义的行为,将破坏任何非普通数据类型的内容,并且生成的文件将是平台依赖,编译器依赖,甚至可能依赖于编译器设置。

C ++没有任何内置的序列化复杂数据的方法。但是,您可能会发现有用的库。例如:

http://www.boost.org/doc/libs/1_40_0/libs/serialization/doc/index.html

答案 4 :(得分:1)

您是否已经了解boost::serialization,它是健壮的,有良好的文档,支持版本控制,如果您想切换到XML格式而不是二进制格式,它会更容易。< / p>

答案 5 :(得分:0)

Fstream.write只是将原始数据写入文件。第一个参数是指向数据起始地址的指针。第二个参数是对象的长度(以字节为单位),因此write知道要写入多少字节。

file.write((char*)&object,sizeof(className));

^ 此行将对象的地址转换为char指针。

historyFile.write((char*)i,sizeof(Referral));

^ 这一行试图将对象(i)转换为char指针(无效)

historyFile.write(i,sizeof(Referral));

^ 当它需要一个char指针时,这一行传递写一个对象。