从二进制文件(C ++)读取结构的恼人错误

时间:2013-04-20 13:32:51

标签: c++ struct binary fopen

我有一个用于读取.ASE文件的类,需要将变量存储在二进制文件中,以便在下次运行应用程序时更快地访问。我将我的信息存储在一个结构中,以使写入过程更容易。这是我使用的结构,在标题中定义:

struct myMesh{
    std::vector<Vector3> vertices;
    std::vector<Vector2> uvs;
    std::vector<Vector3> normals;
    int verticesSize;
    int uvsSize;
    int normalsSize;
};

我还在标题中创建了这个结构的实例来定义变量:

myMesh myInfo;

将数据存储在struct变量中后,我使用C函数编写二进制文件:

std::string path = filename + ".bin";
const char * c = path.c_str();

FILE *pBinaryFile;
pBinaryFile = fopen(c, "wb");
if (pBinaryFile==NULL){
    std::cout << "error";
}
fwrite(&myInfo.vertices, sizeof(myInfo), 1, pBinaryFile);
fclose(pBinaryFile);

要测试二进制文件是否正确创建,我会读取该文件并创建另一个结构实例以显示数据:

myMesh _myInfo;
FILE *theFile;
theFile = fopen(c, "rb");
if (theFile==NULL){
    std::cout << "error";
}
fread(&_myInfo, sizeof(_myInfo), 1, theFile);
fclose(theFile);

这很好用。当我只尝试读取文件时,问题就出现了,只是没有使用写入过程:

/*FILE *pBinaryFile;
pBinaryFile = fopen(c, "wb");

if (pBinaryFile==NULL){
    std::cout << "error";
}

fwrite(&myInfo.vertices, sizeof(myInfo), 1, pBinaryFile);
fclose(pBinaryFile);*/


myMesh _myInfo;
FILE *theFile;
theFile = fopen(c, "rb");
if (theFile==NULL){
    std::cout << "error";
}
fread(&_myInfo, sizeof(_myInfo), 1, theFile);
fclose(theFile);

现在它不起作用。正确获取结构的int变量,但向量变量以???的形式出现有内存错误。我对C ++很新,这可能是一个愚蠢的问题,但我不明白。我也尝试了ofstream和ifstream的C ++函数,我也遇到了同样的问题。

提前致谢。

5 个答案:

答案 0 :(得分:1)

问题在于,这不会使编写过程变得更容易,只会使错误。

您不能使用对fwrite的单个调用(或使用C ++等效项)输出复杂对象,如矢量。你必须这么做。

我无法提供任何具体建议,因为我不知道Vector2Vector3的定义。但基本上你必须输出向量的大小,然后输出每个元素的大小。如果这些元素本身就是复杂的对象,那么你必须特别处理这些元素。你在输入时反向。

答案 1 :(得分:1)

您无法通过询问结构大小来保存结构,因为您保存的数据不是基本的本机类型。您必须编写一个自定义序列函数来编写/读取结构的每个元素。

通过获取向量的长度和单位元素(如Vector3 / Vector2)的大小来保存std :: vector,然后保存它。

另外,我注意到您正在使用C函数来创建和读取文件。这太不酷了。使用C ++请参阅http://www.cplusplus.com/doc/tutorial/files/

希望这有帮助。

答案 2 :(得分:1)

std :: vector将在堆上分配空间,并且只将指针保留在矢量对象本身上。

就像你说的那样:

Type* array = new Type[10];  // array's value is a memory address, NOT the array itself

如果将矢量打印到文件,实际上是在打印地址。这就是为什么如果你在同一次运行中执行保存和加载它没有失败的原因 - 地址没有改变,所以它只是在你离开的地方。但是如果你没有填充向量并将其保存在前面,那么你已经保存的地址将指向任何内容,从而导致非法的内存访问。

要合法地保存矢量,请先保存矢量的大小,然后迭代它并保存每个元素,或使用Boost Serialization

答案 3 :(得分:0)

向量不会将它们的数据保存在向量对象本身中 - 它们保存指向不同堆分配内存的指针。您正在保存和恢复指针,但对向量内的指向数据一无所知。我建议你阅读boost序列化库教程......它应该让你开始朝着富有成效的方向前进。

答案 4 :(得分:0)

如果您遇到问题,请参考以下示例代码(注意:顶点是交错的,不像您的那样分开):

的Vector2D:

#ifndef VECTOR2D_H_
#define VECTOR2D_H_

struct Vector2D
{
    union
    {
        struct { float x,y; };
        struct { float s,t; };
        struct { float u,v; };
        float e[2];
    };
    Vector2D(): x(0.0f),y(0.0f) {}
    Vector2D(const float _x,const float _y): x(_x),y(_y) {}
};

#endif 

的Vector3D:

#ifndef VECTOR3D_H_
#define VECTOR3D_H_

struct Vector3D
{
    union
    {
        struct { float x,y,z; };
        struct { float s,t,r; };
        float e[3];
    };

    Vector3D() :x(0.0f),y(0.0f),z(0.0f) {}
    Vector3D(const float _x,const float _y,const float _z): x(_x),y(_y),z(_z) {}
};

#endif

顶点:

#ifndef VERTEX_H_
#define VERTEX_H_
#include "Vector2D.h"
#include "Vector3D.h"

struct Vertex
{
    Vector3D    pos;
    Vector3D    nrm;
    Vector2D    tex;

    Vertex() {}
    Vertex(const Vector3D& _pos,const Vector3D& _nrm,const Vector2D& _tex)
        :pos(_pos),nrm(_nrm),tex(_tex) {}
};

#endif

目数:

#ifndef MESH_H_
#define MESH_H_
#include <vector>
#include "Vertex.h"
#include <sstream>

struct MyMesh
{
    std::vector<Vertex> verts;
    unsigned numVerts;

    void    WriteOut(std::ostringstream& oss)
    {
        numVerts = verts.size();
        oss.write((const char*)&numVerts,sizeof(numVerts));
        unsigned totalSize = numVerts * sizeof(Vertex);
        oss.write((const char*)verts.data(),totalSize);
    }
    void    ReadIn(std::istringstream& iss)
    {
        iss.read((char*)&numVerts,sizeof(numVerts));
        verts.resize(numVerts);
        iss.read((char*)verts.data(),numVerts*sizeof(Vertex));
    }
};

#endif

测试main.cpp:

#include "Mesh.h"
#include <sstream>
#include <fstream>
#include <iostream>

void PopulateMesh(MyMesh& mesh)
{
    // Fill the mesh with some vertices
    for(int i=0; i<3; ++i)
    {
        Vector3D tempVec(0.0f,i,0.0f);
        Vector2D tempTex(0.0f,i);
        Vertex temp(tempVec,tempVec,tempTex);
        mesh.verts.push_back(temp);
    }
}
void PrintMesh(const MyMesh& mesh)
{
    for(auto i=0u; i<mesh.verts.size(); ++i)
    {
        std::cout << "Position: " << mesh.verts[i].pos.x << ' ' << mesh.verts[i].pos.y << ' ' << mesh.verts[i].pos.z << '\n';
        std::cout << "Normal: " << mesh.verts[i].nrm.x << ' ' << mesh.verts[i].nrm.y << ' ' << mesh.verts[i].nrm.z << '\n';
        std::cout << "Tex Coords: " << mesh.verts[i].tex.u << ' ' << mesh.verts[i].tex.v << "\n\n";
    }
}
void WriteStreamToFile(std::ostringstream& oss)
{
    std::ofstream fout;
    fout.open("test.bin",std::ios_base::binary | std::ios_base::out);
    if(fout.is_open())
    {
        fout.write(oss.str().c_str(),oss.str().size());
        fout.close();
    }
}
void ReadStreamFromFile(std::istringstream& iss)
{
    std::string file;
    std::ifstream fin;
    fin.open("test.bin",std::ios_base::binary | std::ios_base::in | std::ios_base::_Nocreate);
    if(fin.is_open())
    {
        std::getline(fin,file,'\x1A');

        fin.close();
    }
    iss.str(file);
}

int main()
{
    MyMesh outMesh;
    unsigned numMeshes = 1;

    PopulateMesh(outMesh);
    PrintMesh(outMesh);

    // Write to the stream
    std::ostringstream oss(std::ios_base::binary | std::ios_base::out);
    oss.write((const char*)&numMeshes,sizeof(numMeshes));
    outMesh.WriteOut(oss);

    WriteStreamToFile(oss);

    std::istringstream iss(std::ios_base::binary | std::ios_base::in);
    ReadStreamFromFile(iss);

    // Read from the stream
    iss.read((char*)&numMeshes,sizeof(numMeshes));

    MyMesh inMesh;
    inMesh.ReadIn(iss);
    PrintMesh(inMesh);

    return 0;
}