c ++将此对象写入二进制文件

时间:2014-10-10 13:10:05

标签: c++

我在c ++函数中遇到了这个指针的麻烦。 我会把我的类写成二进制文件,所以我写这个函数成员

void Product::saveProducts(){
    fstream file;
    Product temp;
    temp.setId(-1);
    bool flag = false;
    file.open("cigarettes.dat", ios::out | ios::binary);
    if(file.is_open()){
        file.seekg(0, ios::end);
        if(file.tellg()!=0){
            file.seekg(0, ios::beg);
            while(!file.eof()){
                file.read(reinterpret_cast<char *>(&temp), sizeof(temp));
                if(temp.getId() == this->getId()){
                    flag=true;
                    file.seekp(-sizeof(temp),ios::cur);
                    file.write(reinterpret_cast<char *>(this), sizeof(temp));
                    break;
                }
            }
        }
        if(!flag){
            file.write(reinterpret_cast<char *>(this), sizeof(temp));
        }
    }
    file.flush();
    file.close();
}

但是当我尝试用另一个函数成员检索我的存储对象时:

list<Product> Product::loadProducsts(){
    fstream file;
    Product temp;
    list<Product> products;
    file.open("cigarettes.dat", ios::in | ios::binary);
    if(file.is_open()){
        while(!file.eof()){
            file.read(reinterpret_cast<char *>(&temp), sizeof(temp));
            products.push_front(temp);
        }
    }
    file.close();
    return products;
}

我的数组只填充了一个空对象。问题是什么?

1 个答案:

答案 0 :(得分:2)

您的代码存在任何问题,首先是 只是将位转储到文件的事实通常不会 给你一些有用的东西,你可以回读。这个事实 你需要一个reinterpret_cast才能使用它应该给你提示 关闭。另外:

  • 您只打开文件输出,然后尝试从中读取 它。 (打开输出文件会截断它,所以你已经 丢失了以前的所有数据。)

  • 我不确定您对while (!file.eof())的看法, 但这肯定不正确。如果由于某种原因, file.read失败但未触及文件末尾,您将结束 例如,在无限循环中。

  • 您正在使用file.read的结果而不进行验证 该功能有效。

  • 关闭文件而不是结束。这会截断 文件以输出模式打开。

  • 第二个片段的读取循环中的相同问题。如果 文件是空的,你仍然会“读取”一个对象,推动 默认构建tempproducts

不知道Product的样子,也不知道初始内容 该文件,很难说真正发生了什么。但 最有可能的是:

  • 您使用open截断文件。它现在有一个长度 0

  • 由于file.tellg()返回0,您甚至都不会尝试阅读 它。 (如果您尝试阅读它,则会设置错误, 这将使所有连续的操作无操作。)

  • 然后编写单个元素,并关闭文件。

我唯一不太确定的是:在这种情况下,文件 实际上确实包含一个元素。所以当你试着读它时 第一个file.read成功,可能没有设置 eofbit,因为file.read不需要任何展望。而如果 eofbit未设置,我希望你第二次循环, 并将temp中未修改的位推入products秒 时间。

编辑:

FWIW: if 我们假设您处于非常有限的情况 只是将数据位写入磁盘是有效的(哪个 通常意味着你将在以后重新阅读它们 过程,但从来没有从不同的过程),那个 有效idProduct永远不能为-1,你可能是什么 我想做的是:

Product temp;
temp.setId( -1 );   //  This sort of thing should really be handled by a constructor
std::fstream file( "cigartettes.dat", ios::out | ios::in | ios::binary );
while ( file.read( reinterpret_cast<char*>( &temp ), sizeof(temp) && temp.getId() != getId() ) {
}
if ( file.gcount() != 0 ) {
    //      Error somewhere, we ended up reading a partial record
} else if ( temp.getId() == getId() ) {
    file.seekp( -static_cast<int>( sizeof(temp) ) );
} else {
    file.clear();
    file.flush();
}
file.write( reinterpret_cast<char const*>( this ), sizeof(*this) );
file.close();
if ( !file ) {
    //      Something went wrong somewhere...
}

几条评论:

  • 需要打开输入和输出。仅开放 输出意味着1)文件将被截断,2)任何 尝试阅读它会失败。

  • 如果
  • file.read无法读取正确的数字,则会失败 字节。如果它失败了,可能已经读取了一些字节(和 覆盖了Product中的id字段,并保留了当前字段 指针在某个位置,而不是对象的模数 尺寸)。所以你应该使用来自的值检查这个 file.gcount()(返回由...读取的字节数 最后一次未格式化的读操作 - 在读取的情况下 你正在做,这只能与sizeof(Product)不同 如果读取失败。

  • 指定负值以向后搜索时:您有 在做之前将sizeof的结果转换为签名类型 -。否则,你最终会得到一些天文数字 正值,这将导致你试图寻求超越 文件结尾(将失败)。

  • 当读取失败,并且读取的字节数为0时,您就是 到了文件的末尾。然后设置failbit 导致所有未来的操作失败。所以我们必须清除 如果我们要编写扩展数据的错误。 (要是我们 没有达到档案的结尾,当然,没有什么可以做的 清楚的。)

  • 进行双向输入时,读取后必须执行 在写入之前寻求或冲洗。 (不要问我为什么; 这正是标准所说的。)

  • 最后,最好验证文件的状态 在关闭之后,当所有缓冲区都已完全刷新并且 传递给操作系统。如果由于某种原因,写入失败 在某个地方,你想知道它,通知用户 输出的文件已损坏。

我可能会补充说,通常只修改一条记录 文件是将文件复制到新文件,替换或附加 更改记录,然后删除旧文件并重命名 新。尝试修改文件,正如您所做的那样,可能意味着 如果出现问题,你会丢失所有数据。