【问题标题】:Annoying error reading structs from binary file (C++)从二进制文件中读取结构的恼人错误(C++)
【发布时间】:2013-04-20 13:32:51
【问题描述】:

我有一个用于读取 .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);

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

提前致谢。

【问题讨论】:

    标签: c++ struct binary fopen


    【解决方案1】:

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

    就像你说的那样:

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

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

    要合法保存向量,要么先保存向量的大小,然后对其进行迭代并保存每个元素,要么使用Boost Serialization

    【讨论】:

    • 解决了!我已经一一存储了矢量内容和矢量的大小,而不是矢量本身。现在我可以在不写文件的情况下访问它。
    【解决方案2】:

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

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

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

    希望这会有所帮助。

    【讨论】:

      【解决方案3】:

      问题在于,这并没有让写作过程变得更容易,反而会出错。

      您不能使用对 fwrite 的单个调用(或使用 C++ 等效项)来输出复杂的对象,例如向量。你必须努力去做。

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

      【讨论】:

        【解决方案4】:

        向量并不在向量对象本身中保存它们的数据——它们持有指向不同堆分配内存的指针。您正在保存和恢复指针,但对向量内的指向数据不做任何事情。我建议您阅读 boost 序列化库教程......它应该让您开始朝着富有成效的方向发展。

        【讨论】:

        • 正确。在我看来,使用 C++ 序列化本身将使代码更具可读性。
        【解决方案5】:

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

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

        【讨论】:

          猜你喜欢
          • 2011-05-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-05-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多