2010-03-01 17 views
8

Solo porque nunca he leído archivos binarios antes de escribir un programa que lea archivos STL binarios. Uso el miembro de lectura ifstream que toma un parámetro char * a. Para convertir mi estructura en un char * utilizo reinterpret_cast. Pero, por lo que recuerdo, cada libro sobre C++ que leí decía algo así como "no uses reinterpret_cast, excepto que tienes que hacerlo". ¿Cuál sería una mejor manera de leer los datos binarios, no necesariamente directos, pero al final en una estructura y sin reinterpretar_cast?Lectura de datos binarios sin reinterpret_cast

La función principal:

std::ifstream in (cmdline[1].c_str(), std::ios::binary); 

in.seekg(80, std::ifstream::beg); //skip header 

int numTriangle; 
in.read (reinterpret_cast<char*>(&numTriangle), sizeof(int)); //determine number of triangles 
//create triangle data type and read data 
triangle* t = new triangle(); 
for (int i = 0; i < numTriangle; ++i) { 
    in.read(reinterpret_cast<char*>(t), triangle::size); 
    std::cout << *t; // there's an opertor<< for triangle 
} 
delete t; 

in.close(); //close file read from 

Y la estructura de triángulo

//attempt to get the right size of a class without structure padding 
#pragma pack(push) 
#pragma pack(1) 

//standard STL triangle data structure 
struct triangle { 
public: 
    float n[3]; //normals, 4*3=12 bytes 

    float x[3]; //first point of the triangle, 4*3=12 bytes 
    float y[3]; //second point of the triangle, 4*3=12 bytes 
    float z[3]; //third point of the triangle, 4*3=12 bytes 

    long int a; //attributes, 2 bytes 

    static const int size = 12+12+12+12+2; //sum of member variables 
    //static const int size = sizeof(n) + sizeof(x) + sizeof(y) + sizeof(z) + sizeof(a); 
}; 
#pragma pack(pop) 

(pregunta adicional: #pragma pack (1) no funciona con cygwins g ++ - 4 ¿Cómo puedo determinar. el tamaño de la estructura?)

+1

Si está leyendo/escribiendo datos binarios, es una buena idea usar reinterpret_cast. Esto se debe a que el código es inherentemente no portátil y, por lo tanto, reinterpret_cast es una buena forma de auto-documentar el código para indicar que el código no es portátil. Nota: no hay nada incorrecto con su código. No es portátil debido a la forma en que C/C++ define los tipos básicos y su diseño (que puede ser diferente en los indicadores de hardware/OS/compilador/compilador). –

Respuesta

6

Bueno, ese código se ve bien. Incluso te importa el problema del relleno. No veo cómo puedes evitar el casting aquí. Puede hacer esta secuencia:

static_cast<char*>(static_cast<void*>(t)) 

Pero realmente, no hago eso en mi código. Es solo una forma más ruidosa de hacer una reinterpretación_cast directa a char*. (Ver casting via void* instead of using reinterpret_cast).


El tamaño de la estructura se puede determinar usando sizeof. Solo tiene que inicializar al miembro static fuera de la clase dentro del .cpp (sin embargo, el compilador ya no sabe el valor de ::size y no puede alinearlo).
Como alternativa, puede escribirlo como una función de miembro en línea estática. En su cuerpo, el tipo de clase se considera completo y está permitido sizeof (triangle). O simplemente puede usar sizeof como que tiene en el comentario, pero el uso del tipo y no a los miembros (en referencia a los miembros no estáticos se permite que la única forma en C++ 0x):

//standard STL triangle data structure 
struct triangle { 
public: 
    float n[3]; //normals, 4*3=12 bytes 

    float x[3]; //first point of the triangle, 4*3=12 bytes 
    float y[3]; //second point of the triangle, 4*3=12 bytes 
    float z[3]; //third point of the triangle, 4*3=12 bytes 

    long int a; //attributes, 2 bytes 

    static int size() { return sizeof(triangle); } // this way 
    static const int size = sizeof(float[3])*4 + sizeof(long int); // or this way 
}; 

Sin embargo, el segundo camino no es bueno ya que puede olvidarse fácilmente de actualizarlo cuando agrega un miembro.

+0

Gracias. Olvidé borrar la segunda línea, fue tarde ese día que escribí esto. Sin embargo, mi cygwins gcc/g ++/... parece estar roto, ni sizeof, ni sizeof + pragma pack, ni sizeof __attribute __ ((pack)) funciona. – DaClown

+1

Tenga en cuenta que realizar una serialización de estado como esta es intrínsecamente no comercial: intentar leer un archivo en una arquitectura diferente (o incluso la misma con un compilador diferente) puede generar una restauración de datos no utilizados. Además, no he visto un sistema en el que hayan pasado dos bytes en mucho tiempo, lo más probable es que haya cuatro u ocho bytes invertidos en la configuración del procesador y del compilador. Editar: ¿Qué quieres decir con que esas cosas no funcionan? ¿Intentó eliminar el paquete pragma y la función de tamaño estático sugerida? –

+0

De hecho, 'long' * debe * ser mayor que 2 bytes en cualquier sistema que tenga bytes de 8bit. –

2

Pregunta adicional: Eche un vistazo a __attribute__((packed)).

-1

El uso de flujos de archivo de E/S (especialmente binario) es desagradable en mi opinión. Prefiero usar las antiguas funciones C como fopen y fread si fuera usted.

Además, el mapeo de memoria de un archivo es una técnica a la que se le da muy poco amor, IMO. No conozco ninguna biblioteca estándar/portátil que lo soporte, pero si está en Windows sugiero verificar this MSDN article

+0

¿Alguna confirmación en absoluto? ¿Por qué las funciones estándar de C++ son "desagradables"? ¿Qué les falta? ¿Qué hacen las antiguas versiones C mejor? Soy muy escéptico. No he tenido problemas para utilizar flujos de datos de C++ para leer archivos sobre estructuras cuidadosamente mapeadas y hacer todo tipo de cosas con precisión de bits. Sus beneficios de OO, RAII, manejo de excepciones y, presumiblemente, más, son muy importantes. Esto es más como una farsa obstinada que una respuesta utilizable. –