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

c++ this to write object into binary file

本文关键字:二进制文件 对象 c++      更新时间:2023-10-16

我在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;
}

我的数组只填充了一个空对象。怎么了?

您的代码有很多问题,从事实上,仅仅向文件转储位通常不会给你任何有用的东西,你可以读回。事实上你需要reinterpret_cast才能使用它应该会给你小费关闭。此外:

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

  • 我不确定你认为你在用while (!file.eof())做什么,但这肯定是不对的。如果出于某种原因file.read在没有到达文件末尾的情况下失败,您将以例如,在一个无休止的循环中。

  • 并且您使用的是file.read的结果而未进行验证功能正常。

  • 您关闭文件时不在末尾。这会截断以输出模式打开的文件。

  • 第二个代码段的读取循环中也存在同样的问题。如果文件为空,您仍将"读取"一个对象,并按下默认将CCD_ 5构造为CCD_。

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

  • 打开时截断文件。它现在的长度为0.

  • 由于file.tellg()返回0,您甚至从未尝试过读取(如果你试图阅读它,就会设置一个错误,这将使所有连续的操作都没有操作。)

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

我唯一不太确定的是:在这种情况下实际上包含一个元素。所以当你试着读的时候,第一个file.read成功,可能没有设置eofbit,因为file.read不需要任何展望。如果eofbit未设置,我希望您再次循环,并将CCD_ 14中未修改的比特每秒推送到CCD_时间

编辑:

FWIW:如果我们假设您处于非常受限的情况其中仅将数据位写入磁盘是有效的(通常意味着你稍后会在同一时间重读它们进程,但从不来自不同的进程),在有效的Product中,id永远不可能是-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...
}

几个评论:

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

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

  • 当指定负值向后查找时:在执行之前将sizeof的结果转换为签名类型即CCD_ 23。否则,你会得到一些天文数字积极的价值,这将导致你尝试超越文件结尾(将失败)。

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

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

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

我可以补充一下,在文件是将文件复制到新文件,替换或追加更改后的记录,然后删除旧文件并重命名新尝试修改文件可能意味着如果出了问题,你就会丢失所有的数据。