【问题标题】:Write and read array of objects to binary file?将对象数组写入和读取到二进制文件?
【发布时间】:2014-09-25 08:01:28
【问题描述】:

我尝试过使用fstream,虽然显然已编译,但程序崩溃了。 请告诉我上面的代码有什么问题,我应该怎么做来纠正它

我想将二进制文件中miperro5[] 的所有元素恢复到一个新数组miperro6[] 中。可以解决吗?

#include <stdio.h>
#include <iostream>
#include <Perro.hpp>
#include <fstream>

using std::cout;
using std::endl;

int main(int argc, char **argv)
{
    Perro miperro5[5];
    Perro miperro6[5];
    miperro5[2].nombre = "lucky";

    std::ofstream myfile;
    myfile.open("example.bin", std::ios::out | std::ios::app | std::ios::binary);
    for (int i = 0; i < 5; i++){
        myfile.write(reinterpret_cast<char *>(&miperro5[i]), sizeof(Perro));
    }
    myfile.close();

    std::ifstream myfile2;
    myfile2.open("example.bin", std::ios::in | std::ios::binary);
    for (int j = 0; j < 5; j++){
        myfile2.read(reinterpret_cast<char *>(&miperro6[j]), sizeof(Perro));
    }
    myfile2.close();

    for (int z = 0; z < 5; z++){
        cout << miperro6[z].nombre << endl;
    }

    std::cin.get();
    return 0;
}

这是 Perro.hpp

#ifndef PERRO_HPP
#define PERRO_HPP
#include <string>
class Perro
{
public:
    Perro();
    ~Perro();
    void ladrar();
    void comer();
    std::string nombre;
    int edad;
    int arra[2];


};

#endif // PERRO_HPP

【问题讨论】:

  • reinterprete_cast 这个技巧只适用于始终具有相同大小的对象,在Perro 的情况下,永远不知道std::string nombre 的实际大小,甚至这个成员也被初始化为空.您应该在Perro 中创建一个方法,从流中保存和加载对象,在这种情况下,您可以读取std::stringreinterprete_cast 其他2 个成员。

标签: c++ arrays stream


【解决方案1】:

main.cpp

#include <stdio.h>
#include <iostream>
#include <fstream>
#include "Perro.hpp"

using std::cout;
using std::endl;

int main(int argc, char **argv)
{
    Perro miperro5[5];
    Perro miperro6[5];
    miperro5[2].nombre = "lucky";

    std::ofstream myfile;
    myfile.open("example.bin", std::ios::out | std::ios::app | std::ios::binary);
    for (int i = 0; i < 5; i++){
        myfile << miperro5[i];
    }
    myfile.close();

    std::ifstream myfile2;
    myfile2.open("example.bin", std::ios::in | std::ios::binary);
    for (int j = 0; j < 5; j++){
        myfile2 >> miperro6[j];
    }
    myfile2.close();

    for (int z = 0; z < 5; z++){
        cout << miperro6[z].nombre << endl;
    }

    std::cin.get();
    return 0;
}

Perro.hpp

#ifndef PERRO_HPP
#define PERRO_HPP
#include <string>
#include <ostream>
#include <istream>
class Perro
{
public:
    Perro() {};
    ~Perro() {};
    void ladrar();
    void comer();
    std::string nombre;
    int edad;
    int arra[2];
};
std::ostream & operator<<(std::ostream & ostrm, const Perro & src)
{
    // nombre
    std::string::size_type cch = src.nombre.length();
    ostrm // std::string is not a primitive type, so we need to do a (sort of) deep copy.
        .write(reinterpret_cast<const char *>(&cch), sizeof(std::string::size_type))
        << src.nombre.c_str() // or use unformatted output `write`
    ;
    // edad, arra[2]
    ostrm
        .write(reinterpret_cast<const char *>(&src.edad), sizeof(int)) // edad
        .write(reinterpret_cast<const char *>(&src.arra), sizeof(int) * 2) // arra[2]
    ;
    return ostrm;
}
std::istream & operator>>(std::istream & istrm, Perro & dst)
{
    // Read the string back.
    std::string::size_type cchBuf;
    istrm.read(reinterpret_cast<char *>(&cchBuf), sizeof(std::string::size_type));
    char * buf = new char[cchBuf];
    istrm.read(buf, cchBuf);
    dst.nombre.assign(buf, cchBuf);
    delete [] buf;
    // Read other data members of primitive data type.
    istrm
        .read(reinterpret_cast<char *>(&dst.edad), sizeof(int)) // edad
        .read(reinterpret_cast<char *>(&dst.arra), sizeof(int) * 2) // arra[2]
    ;
    // Return the stream so it can be examined on its status.
    return istrm;
}

#endif // PERRO_HPP

【讨论】:

    【解决方案2】:

    问题大概出在这里:

    myfile.write(reinterpret_cast<char *>(&miperro5[i]), sizeof(Perro));
    

    您正在将结构 Perro 写入文件,但您必须确保正确写入,因为通常类似

    struct Foo
    {
        std::string str;
    };
    

    不会像你想的那样写在磁盘上。只有包含纯旧数据 (POD) 的结构才能像您一样编写,其余的您必须手动编写每个字段。如果您想通过对象序列化简化您的生活,请查找 boost::serialization

    【讨论】:

      【解决方案3】:

      您可以使用 fwritefread,仅当类是 POD type 时使用。

      Perro 不是 POD 类型,因为它有一个 std::string 类型的成员变量。

      让我用一个更简单的例子来说明这个问题。假设您有:

      #include <iostream>
      #include <fstream>
      
      struct A
      {
         A(int s) : ip(new int[s]), size(s) {}
         ~A() { delete [] ip; }
         int* ip;
         int size;
      };
      
      int main()
      {
         A a1(10);
         A a2(5);
      
         std::cout << "a1.size: " << a1.size << ", a1.ip: " << a1.ip << std::endl;
      
         std::ofstream myfile;
         myfile.open("example.bin", std::ios::out | std::ios::binary);
         myfile.write(reinterpret_cast<char *>(&a1), sizeof(A));
         myfile.close();
      
         std::ifstream myfile2;
         myfile2.open("example.bin", std::ios::in | std::ios::binary);
         myfile2.read(reinterpret_cast<char *>(&a2), sizeof(A));
         myfile2.close();
      
         std::cout << "a2.size: " << a2.size << ", a2.ip: " << a2.ip << std::endl;
      
         return 0;
      }
      

      当我运行程序时,我得到以下输出:

      a1.size: 10, a1.ip: 0x16b4010 a2.size: 10, a2.ip: 0x16b4010 *** `./test-328' 中的错误:双重释放或损坏(fasttop):0x00000000016b4010 *** 中止

      这里发生的事情是a1.ip 的二进制值被写入文件,而相同的二进制值被读入a2.ip。读取后,a1.ipa2.ip 都指向同一个内存位置。这会导致两个问题:

      1. 析构函数在同一地址调用delete 结束两次。
      2. a2 分配的初始内存永远不会被释放。

      当您的成员变量之一是 std::string 时,也会发生类似的情况。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-23
        • 1970-01-01
        • 2015-07-03
        • 2022-01-28
        • 1970-01-01
        • 2023-03-04
        • 1970-01-01
        相关资源
        最近更新 更多