2010-01-11 2 views
1

Каждый раз, когда я пытаюсь прочитать файл с жесткого диска и отбрасывать данные в структуру, я получаю проблемы с данными, которые не корректно отображаются. Существует ли требование с функцией 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 04 00 00 00 ec 05 51 bf ab aa aa 3e ef 5b f1 be 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 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 f3 f9 2f 42 00 00 ec 05 51 3f ab aa aa 3e ef 5b f1 be 33 33 cb 41 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 80 bf 00 00 00 00 33 33 cb 41 00 00 00 00 00 00 00 33 33 4b 42 00 00 00 f3 f9 2f 42 00 00 00 00 00 00 00 00 f3 f9 2f 42 00 00

+0

Это класс, а не структура. –

+4

В C++ «класс» эквивалентен «struct», кроме видимости по умолчанию «private». – kennytm

+0

Можете ли вы опубликовать свой сохраненный код и/или образец файла? –

ответ

3

Скорее всего, float имеет выравнивание по четыре байта в вашей системе. Это означает, что, поскольку вы используете его в своей структуре, компилятор будет следить за тем, чтобы начало структуры при распределении с использованием обычных методов всегда было кратным четырем байтам. Поскольку необработанный размер вашей структуры составляет 4 * 12 + 2 = 50 байт, ее нужно округлить до следующего кратного из четырех байтов - в противном случае второй элемент массивов этой структуры будет неровным. Таким образом, ваша структура заканчивается 52 байтами, отбрасывая ваш синтаксический анализ.

Если вам нужно разобрать двоичный формат, часто рекомендуется использовать директивы, специфичные для компилятора, для отключения выравнивания или чтения одного поля за раз, чтобы избежать этих проблем.

Например, на MSVC++, вы можете использовать __declspec(align(1)) Edit: На самом деле __declspec(align(X)) может только увеличить ограничение выравнивания. К сожалению. Вам нужно будет либо загружать одно поле за раз, либо сделать дополняющую часть двоичного формата.

+0

Да, я вроде это понял, но есть ли способ его исправить? или мне придется вручную прокручивать набор данных и вручную смещать и читать данные в каждом отдельном ... err ... chunk of class? – Faken

+0

Зависит от компилятора. Какой компилятор вы используете? – bdonlan

+0

Im using visual C++ 2008 – Faken

2

вместо того, чтобы возиться с дополнениями и различиями между платформами, возможно, взглянуть на сериализацию в/из двоичных файлов? Это может быть несколько менее впечатляющим, чем чтение данных прямо в память, но это еще более расширяемо.

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(); 
} 
+0

Следует отметить, что это может привести к снижению производительности в некоторых случаях. – bdonlan

+0

@ bdonlan: Пожалуйста, уточните. –

+0

@ bdonlan: будет ли медленнее читать файл (что очень мало для меня) или медленнее при выполнении операций формования данных в классе (что очень серьезно для меня) – Faken

1

Вы должны знать, что вы бросаете портативность в окне с таким кодом: ваши файлы могут быть несовместимы с новыми версиями вашей программы, если вы компилируете с другим компилятором или для другой системы.

При этом вы можете исправить это, используя sizeof(int[21]) и sizeof(stlFormat[ numTriangles ]), а не жестко закодированные размеры в байтах. Причина, как отмечали другие, байт выравнивания, который ваш компилятор может или не может добавить.

Если это программа, которую могут использовать другие пользователи, или файлы могут использоваться совместно, найдите сериализацию.

+0

STL - стандартный формат файла с фиксированным количеством байтов на треугольник; он не может этого изменить. – Darryl

+0

О, я думал, он имел в виду стандартную библиотеку шаблонов. – Potatoswatter

+0

Я знаю, что у меня проблемы с совместимостью, но поскольку я студент-механик, а не студент-информатик, просто заставить все это работать - это подвиг сам по себе! Если он работает, даже на одном компьютере, я счастлив! (Я, однако, предпринял шаги, чтобы гарантировать, что все компьютеры, с которыми я работаю, запускают процессоры Intel и запускают одну и ту же ОС) – Faken

0

Я думаю, что проблема заключается не столько в чтении каждого отдельного треугольника, сколько в том, что треугольный массив не выложен, как вы думаете. В каждой структуре имеется 50 байт, но выделенная память почти наверняка выложена, как если бы структуры были 52 байта. Рассмотрите возможность чтения в каждой структуре по отдельности. больше

Две точки:

Во-первых, нет такого понятия, как void main в C++. Используйте int main().

Во-вторых, вы, кажется, теряете память. Вам будет лучше вообще использовать объект vector.

+0

Проблема, похоже, заключается в разборе, а не в макете. Из того, что я могу понять, когда я отправляю команду чтения, программа захватывает весь поток данных с моего жесткого диска и выгружает его в заразительный кусок памяти, ничего не делая для этого. Затем он устанавливает схему доступа, такую ​​как резак для печенья, по данным в форме моей структуры. Проблема в том, что резак для печенья составляет 52 байта, а не 50, из-за того, что все хорошо и даже с тем, как процессор обращается к памяти. моя проблема заключается в разборе. BTW, void main() действителен в Visual C++ (но да, это не стандарт) – Faken

+0

Я бы назвал это проблемой макета, а не проблемой синтаксического анализа. Если вы прочитаете каждые 50 байтов в треугольник, я бы ожидал, что он сработает, поэтому я предложил прочитать структуры по отдельности. –

1

IMO вы действительно должны явно читать треугольники напрямую (десериализация) вместо того, чтобы кастовать байты. Это поможет вам избежать проблем с переносимостью и производительностью. Если вы выполняете множество вычислений с этими треугольниками после их чтения, производительность, получаемая при использовании нестандартного макета памяти, может быть нетривиальной.

Заменить строку "stlFile.read (reinterpret_cast (треугольники), (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); 
} 

Это займет немного больше кода, и немного больше времени, чтобы прочитать в треугольниках, но вы избежите несколько потенциальных проблем.

Обратите внимание, что для написания треугольников также требуется аналогичный код, чтобы избежать непреднамеренно выписывания некоторых дополнений.

0

Хранение структуры полностью сразу не переносится, если вы не проявляете особой осторожности к флагов, специфичных для компилятора, и все компиляторы и архитектуры могут все еще не разрешать один и тот же двоичный формат. Сохранение поля (например, число с плавающей запятой) в то время лучше, но оно все еще не переносимо из-за проблем с endianess и, возможно, с разными типами данных (например, размер sizeof (long) в вашей системе).

Чтобы сохранить целые числа безопасно и переносимо, вам необходимо отформатировать их в один момент в буфер символов, который затем будет выписан в файл. Например.

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.

Поскольку никаких указателей на целые типы для символа или наоборот, код полностью переносится.Выполнение этого же для типов с плавающей точкой требует особой осторожности и приведения указателя, так что значение может обрабатываться как целое число, так что это смещение бит. Здесь я не буду подробно останавливаться на этом.

Хотя это решение кажется чрезвычайно болезненным, вам нужно всего лишь написать несколько вспомогательных функций, чтобы сделать его приемлемым. В качестве альтернативы, особенно если используемый точный формат не имеет для вас значения, вы можете использовать существующую библиотеку сериализации. Boost.Serialization - довольно хорошая библиотека для этого.

Смежные вопросы