2009-12-21 22 views
5

He escrito una biblioteca C++ que guarda mis datos (una colección de estructuras personalizadas, etc.) en un archivo binario. Actualmente uso (es decir, crear y consumir) los archivos localmente, en mi máquina con Windows (XP). Para simplificar, pensemos en la biblioteca en dos partes: un escritor (Crea los archivos) y un lector o consumidor (simplemente lee los datos de los archivos).Archivos binarios y compatibilidad multiplataforma

Recientemente, sin embargo, me gustaría también consumen (es decir, leer) los archivos de datos que he creado en mi máquina XP, en mi máquina Linux. Debo señalar en esta etapa que ambas máquinas son PC (por lo que tienen la misma endianess, etc.).

Puedo construir un lector (y compilar para Linux [Ubuntu 9.10 para ser precisos]), ya que soy el creador de la biblioteca. Mi pregunta, antes de embarcarse en este camino (de la construcción del lector, etc.) es:

Suponiendo que he construido con éxito al lector para Linux,

¿Puedo simplemente copiar al otro lado, los archivos que estaban crearon en el Windows (XP) máquina a la máquina Linux (Ubuntu 9.10) y use el lector de Linux para leer con éxito el archivo copiado sobre el archivo?

+0

Debe definir lo que quiere decir con "archivo binario" un poco más. ¿Escribes cosas como datos de 'struct', etc., usando' fwrite'? ¿Descompone todo en bytes y luego escribe los datos? –

+1

¿Qué pasa con todo el GRITO? Puedes usar asteriscos para ** énfasis ** y es más fácil de leer ... – danio

+0

@danio viene de DOS, DONDE TODAS LAS COSAS SON CAPITAL – zeitue

Respuesta

14

Para los archivos para ser compatible binario:

  • endianness deben coincidir (como lo hace para usted)
  • orden de empaquetamiento
  • campo de bits debe ser el mismo
  • tamaños y de signo de tipos deben ser los mismos
  • el compilador debe tomar las mismas decisiones sobre el relleno y alineación

es ciertamente posible f o que se cumplan todas estas condiciones, o que usted no golpee ningún caso para el que no lo esté. Sin embargo, como mínimo, agregaría algunos controles de cordura y/o miembros centinela para detectar problemas.

+0

Hola sombra de luna, gracias por los comentarios. ¿Podrías elaborar algo más? Cuando tengas tiempo, usando una clase simple que contenga un std :: vector , seré capaz de entender sin ambages lo que quieres decir con chequeo de cordura y/o miembros centinelas. La única forma en que puedo pensar en implementar estos controles sería usando las constantes definidas en los límites. ¿Eso es lo que querías decir? - ¿O tal vez tienes un enfoque más elegante? –

+0

Además, no estoy seguro de cómo verificar si los requisitos 2, 3 y 4 (que mencionó anteriormente) son ciertos. Construyo usando VS2008 en XP, y usando gcc 4.4.1 en Ubuntu, ¿algún consejo sobre cómo puedo verificar que NO se violen estos requisitos? –

+2

@Stick it: me refiero a "miembros centinelas" para que las estructuras de nivel superior escritas en su archivo contengan un miembro con un valor constante conocido, y también coloque una al final del archivo; en el momento de la carga, verifique que estos miembros contengan el valor que espera, esto debería tener problemas con los tamaños/relleno que difieren entre los compiladores. – moonshadow

2

Los archivos binarios deben ser compatibles en todas las máquinas con la misma endianess.

El problema que puede tener en su código es el tamaño de las entradas, no necesariamente puede suponer que el compilador en diferentes sistemas operativos tiene el mismo tamaño int. Así que, o copiar bloques de bytes y los echó, o Int16 uso, etc. int32

1

Si:

  • las máquinas tienen el mismo endianess (como ha afirmado que tienen) y
  • hacer abrir la fluye en modo binario, ya que el modo texto puede hacer cosas divertidas, por ejemplo con la línea de los fines y
  • que haya programado limpia para que no se tropieza con cosas como el definido por la implementación alineaciones, tamaños, tipos de datos y embalaje estructura,

entonces sí, los archivos debe ser portátil.

El tercer punto es lo que hace que un formato de archivo sea "portátil". Dependiendo de qué tipo de datos tenga en sus estructuras, puede ser muy fácil o un poco complicado. Bitfields, o los datos que se reinterpretan de un tipo diferente son especialmente difíciles.

1

Puede considerar echar un vistazo al Boost Serialization Library. Se ha reflexionado mucho sobre esto y manejará muchas de las posibles incompatibilidades multiplataforma para usted. Por supuesto, es posible que sea excesivo para su caso de uso en particular, especialmente si ya tiene sus escritores & lectores implementados.

1

Las estructuras son no un formato de archivo, y no debe intentar utilizarlas como tales.

Al intentar hacer que las estructuras funcionen con fread y fwrite, hay una gran cantidad de hacks para hacerlo funcionar. Intercambia enteros en byte para que puedas compartir archivos entre máquinas little-endian y big-endian. Cambia sus estructuras para usar tipos enteros de ancho fijo, para que pueda compartir entre máquinas con diferentes tamaños de palabras (como entre máquinas x86 y x64). Agregue pragmas específicos del compilador para controlar el relleno de las estructuras para compartir entre las versiones del compilador.

Funciona, pero es feo. Sin mencionar, es fácil equivocarse.

Al igual que la recomendación en The byte order fallacy, una idea mucho mejor es escribir código para leer/escribir los campos individualmente. Al escribir su propio código, puede asegurarse de que no hay relleno, y puede elegir tamaños enteros independientemente del tamaño local de los enteros, y puede admitir ambos endiannesses sin byte-swapping (leyendo/escribiendo los bytes de un entero por separado).

A diferencia del enfoque hacky, es difícil equivocarse. Además, debido a que no confía en ningún compilador o comportamiento específico de arquitectura, su código funcionará en todos los compiladores y arquitecturas, o ninguno. Si lo haces bien, no deberías tener ningún error específico de la plataforma.

Hay un inconveniente; leer/escribir individualmente los campos será más lento que solo usar fread/fwrite directamente. Puede configurar un búfer (uint8_t buffer[]) y escribir la totalidad de los datos en él, y luego escribir todo de una vez, lo que podría ayudar, pero aún será más lento (porque aún tendría que mover los campos a el buffer uno a la vez), pero para la mayoría de los propósitos, seguirá siendo lo suficientemente rápido (con excepciones, sistemas embebidos/en tiempo real o computación de alto rendimiento).

Cuestiones relacionadas