Estoy tratando de analizar un archivo bmp con fread() y cuando me pongo a analizar, se invierte el orden de mi bytes.

typedef struct{
    short magic_number;
    int file_size;
    short reserved_bytes[2];
    int data_offset;
}BMPHeader;
    ...
BMPHeader header;
    ...

Los datos hexadecimales es 42 4D 36 00 03 00 00 00 00 00 36 00 00 00;
Yo soy de la carga de los datos hexadecimales en la estructura por fread(&header,14,1,fileIn);

Mi problema es donde la magia número debe ser 0x424d //'BM' fread() se despliega la bytes a ser 0x4d42 //'MB'

¿Por qué fread() hacer esto y cómo puedo solucionarlo;

EDIT: Si yo no era lo suficientemente específicos, tengo que leer la totalidad de la porción de datos hexadecimales en la estructura no sólo el número mágico. Sólo he recogido el número mágico como un ejemplo.

… pan se mete con su mordida orden? Intenta picar?
No es que fread en lugar de bread para su título?
lo siento. Todavía tengo que conseguir el uso de los Leones Auto correcto. Me fijo
endianness…..

OriginalEl autor Chase Walden | 2011-12-19

3 Comentarios

  1. 14

    Esto no es la culpa de fread, pero de la CPU, que es (aparentemente) de little-endian. Es decir, la CPU trata del primer byte en un short valor como el baja 8 bits, en lugar de (como parece que han esperado) los 8 bits.

    Cada vez que leas un formato de archivo binario, debe convertir explícitamente desde el formato de archivo del peso de la CPU nativo de peso. Haces eso con funciones como estas:

    /* CHAR_BIT == 8 assumed */
    uint16_t le16_to_cpu(const uint8_t *buf)
    {
       return ((uint16_t)buf[0]) | (((uint16_t)buf[1]) << 8);
    }
    uint16_t be16_to_cpu(const uint8_t *buf)
    {
       return ((uint16_t)buf[1]) | (((uint16_t)buf[0]) << 8);
    }

    De hacer su fread en un uint8_t búfer de tamaño adecuado y, a continuación, copiar manualmente todos los bytes de datos a su BMPHeader struct, la conversión necesaria. Sería algo como esto:

    /* note adjustments to type definition */
    typedef struct BMPHeader
    {
        uint8_t magic_number[2];
        uint32_t file_size;
        uint8_t reserved[4];
        uint32_t data_offset;
    } BMPHeader;
    
    /* in general this is _not_ equal to sizeof(BMPHeader) */
    #define BMP_WIRE_HDR_LEN (2 + 4 + 4 + 4)
    
    /* returns 0=success, -1=error */
    int read_bmp_header(BMPHeader *hdr, FILE *fp)
    {
        uint8_t buf[BMP_WIRE_HDR_LEN];
    
        if (fread(buf, 1, sizeof buf, fp) != sizeof buf)
            return -1;
    
        hdr->magic_number[0] = buf[0];
        hdr->magic_number[1] = buf[1];
    
        hdr->file_size = le32_to_cpu(buf+2);
    
        hdr->reserved[0] = buf[6];
        hdr->reserved[1] = buf[7];
        hdr->reserved[2] = buf[8];
        hdr->reserved[3] = buf[9];
    
        hdr->data_offset = le32_to_cpu(buf+10);
    
        return 0;
    }

    Hacer no asumir que la CPU del peso es el mismo que el formato de archivo del incluso si se puede saber de un hecho que ahora son los mismos; escribir las conversiones de todos modos, para que en el futuro el código de trabajo, sin modificación, en un CPU con la frente endianness.

    Puede hacer la vida más fácil para ti mediante el uso de la anchura fija <stdint.h> tipos, mediante unsigned tipos, a menos de ser capaz de representar los números negativos es absolutamente necesario, y por no usando números enteros cuando matrices de caracteres va a hacer. He hecho todas estas cosas en el ejemplo anterior. Usted puede ver que usted no tiene que molestar a endian convertir el número mágico, porque la única cosa que usted necesita hacer con él es la prueba de magic_number[0]=='B' && magic_number[1]=='M'.

    De conversión en la dirección opuesta, por cierto, se parece a esto:

    void cpu_to_le16(uint8_t *buf, uint16_t val)
    {
       buf[0] = (val & 0x00FF);
       buf[1] = (val & 0xFF00) >> 8;
    }
    void cpu_to_be16(uint8_t *buf, uint16_t val)
    {
       buf[0] = (val & 0xFF00) >> 8;
       buf[1] = (val & 0x00FF);
    }

    Conversión de 32-/64-bits cantidades deja como ejercicio.

    Si vas a utilizar uint32_t file_size, el peso se fija en LE, por lo que hay en razón de no uso uint16_t magic_number.
    No, porque tú no fread directamente en el BMPHeader objeto. Usted fread en uint8_t buf[sizeof(BMPHeader)] y luego copiar manualmente a través de cada campo, la conversión cuando corresponda; por lo tanto el uso de un dos-cadena de caracteres para el número mágico que evita una conversión. También yo diría que es más natural para tratar el «número mágico» como un dos-cadena de caracteres de todos modos (en este caso).
    ¿cómo copiar los datos en este caso?
    ¿Cómo sabes que para convertir la LE->si usted no mira magic_number a ver si es 0x424D o 0x4D42?
    Que no se haga esa pregunta. siempre convertir, de los definidos peso del archivo (ARCHIVO en este caso) a sea cual sea la CPU quiere. Usted no necesita saber lo que endianness la CPU es hacer la conversión, a mi _to_cpu funciones de trabajar independientemente.

    OriginalEl autor zwol

  2. 2

    Supongo que esto es un endian problema. es decir, Usted está poniendo los bytes 42 y 4D en su short valor. Pero su sistema es little endian (yo podría tener el nombre equivocado), que realmente lee los bytes (dentro de un multi-byte de tipo entero) de izquierda a derecha en lugar de derecha a izquierda.

    Demostrado en este código:

    #include <stdio.h>
    
    int main()
    {
        union {
            short sval;
            unsigned char bval[2];
        } udata;
        udata.sval = 1;
        printf( "DEC[%5hu]  HEX[%04hx]  BYTES[%02hhx][%02hhx]\n"
              , udata.sval, udata.sval, udata.bval[0], udata.bval[1] );
        udata.sval = 0x424d;
        printf( "DEC[%5hu]  HEX[%04hx]  BYTES[%02hhx][%02hhx]\n"
              , udata.sval, udata.sval, udata.bval[0], udata.bval[1] );
        udata.sval = 0x4d42;
        printf( "DEC[%5hu]  HEX[%04hx]  BYTES[%02hhx][%02hhx]\n"
              , udata.sval, udata.sval, udata.bval[0], udata.bval[1] );
        return 0;
    }

    Da el siguiente resultado

    DEC[    1]  HEX[0001]  BYTES[01][00]
    DEC[16973]  HEX[424d]  BYTES[4d][42]
    DEC[19778]  HEX[4d42]  BYTES[42][4d]

    Así que si quieres ser portátil, usted tendrá que detectar el endian-dad de su sistema y, a continuación, hacer un byte shuffle si es necesario. Habrá un montón de ejemplos de la ronda de internet de intercambio de bytes alrededor.

    La siguiente pregunta:

    Sólo te pido porque mi tamaño del archivo es de 3 en lugar de 196662

    Esto es debido a la memoria los problemas de alineación. 196662 es la bytes 36 00 03 00 y 3 es la bytes 03 00 00 00. La mayoría de los sistemas de tipos como int etc a no ser dividida en múltiples memoria words. Así que intuitivamente se piensa que su estructura se presenta im memoria como:

                              Offset
    short magic_number;       00 - 01
    int file_size;            02 - 05
    short reserved_bytes[2];  06 - 09
    int data_offset;          0A - 0D

    PERO en un sistema de 32 bits que significa files_size tiene 2 bytes en el mismo word como magic_number y dos bytes en el siguiente word. La mayoría de los compiladores no se pondrán de pie para esto, así que la forma en que la estructura se presenta en la memoria es en realidad como:

    short magic_number;       00 - 01
    <<unused padding>>        02 - 03
    int file_size;            04 - 07
    short reserved_bytes[2];  08 - 0B
    int data_offset;          0C - 0F

    Así que cuando usted lee su secuencia de bytes en el 36 00 está entrando en su zona de relleno que sale de su file_size como conseguir el 03 00 00 00. Ahora bien, si usted utiliza fwrite para crear este tipo de datos debería haber sido ACEPTAR como los bytes de relleno habría sido escrito. Pero si su entrada es siempre va a ser en el formato que usted ha especificado no es apropiado para leer toda la estructura como un todo con fread. En su lugar, debe leer cada uno de los elementos de forma individual.

    Lo siento, pulse guardar demasiado temprano. Todo lo que hay ahora
    +1 para la demostración, aunque sería bueno hacer el «little-endian» suposición aquí explícita.
    ¿Esto sólo afecta a un short? Sólo te pido porque mi tamaño del archivo es de 3 en lugar de 196662
    No, los efectos de todos los tipos enteros mayores que 1 byte, por lo que short, int, long, y long long. Si utilizas mi código como base para la depuración, puede que tenga que quitar/cambiar la h caracteres en el printf formatos. h es para cortos, hh es para unsigned char. Compruebe man 3 printf para más detalles.
    Yo no uso el h caracteres. Todavía tengo problemas con la file_size

    OriginalEl autor Sodved

  3. 0

    La escritura de una estructura a un archivo es altamente no-portátil-es más seguro simplemente para no intentar hacerlo todo. El uso de una estructura como esta está garantizado para trabajar sólo si (a) la estructura es escrito y leído como una estructura (nunca una secuencia de bytes) y b) siempre por escrito y leído sobre el mismo (tipo de) de la máquina. No sólo hay «endian» problemas con diferentes CPUs (que es lo que parece te has topado), también hay un «alineamiento» de los problemas. Diferentes implementaciones de hardware tienen diferentes reglas sobre la colocación de los números enteros sólo en incluso de 2 bytes o incluso de 4 bytes o incluso los límites de 8 bytes. El compilador es plenamente consciente de todo esto, y se inserta oculto bytes de relleno en su estructura por lo que siempre funciona bien. Pero como resultado de la oculta bytes de relleno, que no es seguro asumir una estructura de bytes que se establecen en la memoria como usted piensa que son. Si eres muy afortunado, usted trabaja en un equipo que utiliza big-endian orden de los bytes y no tiene restricciones de alineación en todos, así que usted puede poner sus estructuras directamente sobre los archivos para que funcionen. Pero usted probablemente no tiene esa suerte, ciertamente programas que necesitan ser «portable» para diferentes máquinas tienen para evitar tratando de sentar las estructuras directamente sobre cualquier parte de cualquier archivo.

    gracias por compartir tus conocimientos. esto tiene sentido, y yo le cambie el código en el futuro, si me elige para que sea más portátil.
    Blender 3d basa su fileformat en la lectura/escritura de las estructuras de archivos, incluso la gestión de los punteros, endian y de 32/64 bits de conversión. Su no-trivial, pero yo no diría – «no lo hagas»
    No estoy de acuerdo completamente. Correctamente la lectura/escritura de las estructuras no es trivial y fácil equivocarse en sutiles de la plataforma formas específicas (tales como no ser capaz de compartir archivos entre máquinas). La escritura de la plataforma agnóstica para leer/escribir los campos manualmente es trivial y difíciles de conseguir y el mal, y por no hablar de que va a trabajar en todas partes o en ninguna parte. La lectura/escritura de las estructuras correctamente no es tan difícil, pero es sin duda más difícil para ningún beneficio.
    Su estado trabajando en la Licuadora por 20 años, dando muy rápido e / s de archivos. de acuerdo que hay «ningún beneficio», Si usted tiene muchos de diferentes estructuras (100 o más, que cambian a medida que el software se ha mejorado), manual de lectura/escritura toma un poco de esfuerzo de escribir y mantener. Hay algunas restricciones en estructuras (punteros/dobles deben ser de 8 bytes alineados, incluso en los sistemas de 32 bits), pero esto puede ser comprobado y garantizado para ser portátil. Así, mientras que usted tiene un punto, en la práctica se puede hacer funcionar bastante bien. Para un solo archivo de encabezado – de acuerdo en que no vale la pena hacerlo.

    OriginalEl autor Chuck Kollars

Dejar respuesta

Please enter your comment!
Please enter your name here