每当我尝试从硬盘驱动器读取文件并将数据转换为结构时,我最终都会遇到数据无法正常投射的问题。是否需要使用reinterpret_cast()函数,该函数要求结构中的字节数是4个字节的倍数?如果没有,我做错了什么?如果是这样,我该如何解决这个问题?
我的结构如下所示:(它们是50个字节的块)
class stlFormat
{
public:
float normalX, normalY, normalZ;
float x1,y1,z1;
float x2,y2,z2;
float x3,y3,z3;
char byte1, byte2;
};
我的其余代码:
void main()
{
int size;
int numTriangles;
int * header = new int [21]; // size of header
ifstream stlFile ("tetrahedron binary.STL", ios::in|ios::binary|ios::ate);
size = stlFile.tellg(); // get the size of file
stlFile.seekg(0, ios::beg); //read the number of triangles in the file
stlFile.read(reinterpret_cast<char*>(header), 84);
numTriangles = header[20];
stlFormat * triangles = new stlFormat [numTriangles]; //create data array to hold vertex data
stlFile.seekg (84, ios::beg); //read vertex data and put them into data array
stlFile.read(reinterpret_cast<char*>(triangles), (numTriangles * 50));
cout << "number of triangles: " << numTriangles << endl << endl;
for (int i = 0; i < numTriangles; i++)
{
cout << "triangle " << i + 1 << endl;
cout << triangles[i].normalX << " " << triangles[i].normalY << " " << triangles[i].normalZ << endl;
cout << triangles[i].x1 << " " << triangles[i].y1 << " " << triangles[i].z1 << endl;
cout << triangles[i].x2 << " " << triangles[i].y2 << " " << triangles[i].z2 << endl;
cout << triangles[i].x3 << " " << triangles[i].z3 << " " << triangles[i].z3 << endl << endl;
}
stlFile.close();
getchar();
}
只为你约翰,虽然它相当难以理解。它是十六进制格式。
73 6f 6c 69 64 20 50 61 72 74 33 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 04 00 00 00 ec 05 51 bf ab aa aa 3e ef 5b f1 be 00 00 00 00 00 00 00 00 f3 f9 2f 42 33 33 cb 41 80 e9 25 42 9a a2 ea 41 33 33 cb 41 00 00 00 00 00 00 00 00 00 00 00 00 00 ab aa aa 3e ef 5b 71 3f 33 33 4b 42 00 00 00 00 f3 f9 2f 42 33 33 cb 41 80 e9 25 42 9a a2 ea 41 00 00 00 00 00 00 00 00 f3 f9 2f 42 00 00 ec 05 51 3f ab aa aa 3e ef 5b f1为33 33 cb 41 00 00 00 00 00 00 00 00 33 33 cb 41 80 e9 25 42 9a a2 ea 41 33 33 4b 42 00 00 00 00 f3 f9 2f 42 00 00 00 00 00 00 00 00 80 bf 00 00 00 00 33 33 cb 41 00 00 00 00 00 00 00 00 33 33 4b 42 00 00 00 00 f3 f9 2f 42 00 00 00 00 00 00 00 00 f3 f9 2f 42 00 00
答案 0 :(得分:3)
最有可能的是,float
在您的系统上具有四个字节的对齐方式。这意味着,因为您在结构中使用它,编译器将确保使用常规方法分配时结构的开始始终是四个字节的倍数。由于结构的原始大小是4 * 12 + 2 = 50个字节,因此需要将其四舍五入到下一个四个字节的倍数 - 否则,此结构的数组的第二个元素将是未对齐的。因此,您的结构最终会占用52个字节,从而导致解析失败。
如果需要解析二进制格式,通常最好使用特定于编译器的指令来禁用对齐,或者一次读取一个字段,以避免这些问题。
例如,在MSVC ++上,您可以使用编辑:实际上__declspec(align(1))
__declspec(align(X))
只会增加对齐限制。哎呀。您需要一次加载一个字段,或者使用二进制格式的填充部分。
答案 1 :(得分:3)
我使用自己喜欢的文本编辑器(editpadpro)将您在OP中发布的文件保存为名为“c:\ work \ test.bin”的二进制文件,将您的代码编辑为以下内容,并且(显然)已生成正确的(预期的)输出。请试一试。
#include <cstdlib>
#include <iostream>
#include <fstream>
using namespace std;
#pragma pack( push, 1 )
class stlFormat
{
public:
float normalX, normalY, normalZ;
float x1,y1,z1;
float x2,y2,z2;
float x3,y3,z3;
char byte1, byte2;
};
#pragma pack( pop )
struct foo
{
char c, d, e;
};
void main()
{
size_t sz = sizeof(foo);
int size;
int numTriangles;
int * header = new int [21]; // size of header
ifstream stlFile ("c:\\work\\test.bin", ios::in|ios::binary|ios::ate);
size = stlFile.tellg(); // get the size of file
stlFile.seekg(0, ios::beg); //read the number of triangles in the file
stlFile.read(reinterpret_cast<char*>(header), 84);
numTriangles = header[20];
stlFormat * triangles = new stlFormat [numTriangles]; //create data array to hold vertex data
stlFile.seekg (84, ios::beg); //read vertex data and put them into data array
stlFile.read(reinterpret_cast<char*>(triangles), (numTriangles * 50));
cout << "number of triangles: " << numTriangles << endl << endl;
for (int i = 0; i < numTriangles; i++)
{
cout << "triangle " << i + 1 << endl;
cout << triangles[i].normalX << " " << triangles[i].normalY << " " << triangles[i].normalZ << endl;
cout << triangles[i].x1 << " " << triangles[i].y1 << " " << triangles[i].z1 << endl;
cout << triangles[i].x2 << " " << triangles[i].y2 << " " << triangles[i].z2 << endl;
cout << triangles[i].x3 << " " << triangles[i].z3 << " " << triangles[i].z3 << endl << endl;
}
stlFile.close();
getchar();
}
答案 2 :(得分:2)
而不是摆弄平台之间的填充和差异,或许可以查看二进制文件的序列化?它可能在某种程度上不那么高效,然后将数据直接读入内存,但它更具扩展性。
答案 3 :(得分:1)
你应该知道你正在使用那种代码抛弃窗口的可移植性:如果使用不同的编译器或不同的系统编译,你的文件可能与程序的新版本不兼容。
也就是说,您可以使用sizeof( int[21] )
和sizeof( stlFormat[ numTriangles ] )
来解决此问题,而不是以字节为单位的硬编码大小。正如其他人所指出的那样,原因是编译器可能添加或不添加的对齐字节。
如果这是其他人可能使用的程序或可能共享的文件,请查找序列化。
答案 4 :(得分:1)
IMO你真的应该直接明确地读取三角形(反序列化)而不是强制转换字节。这样做可以帮助您避免可移植性和性能问题。如果你在阅读它们之后用这些三角形做了很多计算,那么使用非标准内存布局的性能可能是非常重要的。
替换行“stlFile.read(reinterpret_cast(triangles),(numTriangles * 50));”有了这个:
for (int i = 0; i < numTriangles; i++)
{
stlFile.read((char*)&triangles[i].normalX, sizeof(float));
stlFile.read((char*)&triangles[i].normalY, sizeof(float));
stlFile.read((char*)&triangles[i].normalZ, sizeof(float));
stlFile.read((char*)&triangles[i].x1, sizeof(float));
stlFile.read((char*)&triangles[i].y1, sizeof(float));
stlFile.read((char*)&triangles[i].z1, sizeof(float));
stlFile.read((char*)&triangles[i].x2, sizeof(float));
stlFile.read((char*)&triangles[i].y2, sizeof(float));
stlFile.read((char*)&triangles[i].z2, sizeof(float));
stlFile.read((char*)&triangles[i].x3, sizeof(float));
stlFile.read((char*)&triangles[i].y3, sizeof(float));
stlFile.read((char*)&triangles[i].z3, sizeof(float));
stlFile.read(&triangles[i].byte1, 1);
stlFile.read(&triangles[i].byte2, 1);
}
需要更多的代码和更多的时间来阅读三角形,但你会避免一些潜在的麻烦。
请注意,编写三角形也需要类似的代码,以避免无意中写出一些填充。
答案 5 :(得分:0)
我认为问题不在于每个三角形的读数,因为三角形阵列没有像你想象的那样排列。每个结构中似乎有50个字节,但分配的内存几乎可以确定,就像结构是52个字节一样。考虑单独阅读每个结构。
还有两点:
首先,C ++中没有void main
这样的东西。使用int main()
。
其次,你似乎在泄露记忆。一般来说,使用vector
工具会更好。
答案 6 :(得分:0)
一次性完全存储结构是不可移植的,除非您特别注意编译器特定的标志,并且所有编译器和体系结构可能仍然不允许相同的二进制格式。一次存储一个字段(例如浮点数)更好,但由于字节序问题和可能的不同数据类型(例如系统上的sizeof(long)),仍然不可移植。
为了安全和便携地保存整数,您必须将它们一次格式化为一个char缓冲区,然后将其写入文件。 E.g。
char buf[100]; // Extra space for more values (instead of only 4 bytes)
// Write a 32 bit integer value into buf, using big endian order
buf[0] = value >> 24; // The most significant byte
buf[1] = value >> 16;
buf[2] = value >> 8;
buf[3] = value; // The least significant byte
同样,回读必须一次完成一个字节:
// Converting the pointer to unsigned to avoid sign extension issues
unsigned char* ubuf = reinterpret_cast<unsigned char*>(buf);
value = ubuf[0] << 24 | ubuf[1] << 16 | ubuf[2] << 8 | ubuf[3];
如果需要小端序,则反转buf和ubuf的索引顺序。
因为没有将整数类型的指针转换为char或反之亦然,所以代码是完全可移植的。对浮点类型执行相同操作需要格外小心并且指针强制转换,以便可以将值作为整数处理,以便进行位移。我不会在这里详细介绍。
虽然这个解决方案使用起来非常痛苦,但您只需编写一些辅助函数即可使其容忍。或者,特别是如果使用的确切格式对您无关紧要,您可以使用现有的序列化库。 Boost.Serialization是一个相当不错的库。