Pregunta

Estoy trabajando en un formato de archivo que deben ser escritos y leídos en varios sistemas operativos diferentes y computadoras. Algunos de estos equipos deben ser máquinas x86, x86-64 otros. Pueden existir algunos otros procesadores, pero no estoy preocupado por ellos sin embargo .

Este formato de archivo debe contener varios números que se leen como sigue:

struct LongAsChars{
    char c1, c2, c3, c4;
};

long readLong(FILE* file){
    int b1 = fgetc(file);
    int b2 = fgetc(file);
    int b3 = fgetc(file);
    int b4 = fgetc(file);
    if(b1<0||b2<0||b3<0||b4<0){
        //throwError
    }

    LongAsChars lng;
    lng.c1 = (char) b1;
    lng.c2 = (char) b2;
    lng.c3 = (char) b3;
    lng.c4 = (char) b4;

    long* value = (long*) &lng;

    return *value;
}

y escrita como:

void writeLong(long x, FILE* f){
    long* xptr = &x;
    LongAsChars* lng = (LongAsChars*) xptr;
    fputc(lng->c1, f);
    fputc(lng->c2, f);
    fputc(lng->c3, f);
    fputc(lng->c4, f);
}

A pesar de esto parece estar trabajando en mi equipo, me preocupa que tal vez no en otros, o que el formato de archivo puede llegar a ser diferentes a través de los ordenadores (computadoras de 32 bits vs 64 bits, por ejemplo). ¿Estoy haciendo algo mal? ¿Cómo debo aplicar mi código para utilizar un número constante de bytes por número?

¿Debo usar fread (lo que, posiblemente, hacer mi código más rápido también) en su lugar?

¿Fue útil?

Solución

Utilice los tipos de stdint.h para asegurarse de obtener el mismo número de bytes de entrada y salida.

A continuación, te acaba de dejar de tratar con problemas endianness, que probablemente no tiene código Realmente manejar.

La serialización de largo con un carbón alias * te deja con diferentes órdenes de bytes en el archivo escrito para plataformas con diferentes endianess.

Usted debe descomponer el bytes algo así:

char c1 = (val >>  0) & 0xff;
char c2 = (val >>  8) & 0xff;
char c3 = (val >> 16) & 0xff;
char c4 = (val >> 24) & 0xff;

y recomponer a continuación, usando algo como:

val = (c4 << 24) |
      (c3 << 16) |
      (c2 <<  8) |
      (c1 <<  0);

Otros consejos

También es posible que tenga problemas con endianness . Por qué no utilizar algo así como NetCDF o HDF , que se ocupan de los problemas de portabilidad que puedan surgir?

En lugar de utilizar estructuras con personajes en ellos, considerar un enfoque más matemático:

long l  = fgetc() << 24;
     l |= fgetc() << 16;
     l |= fgetc() <<  8;
     l |= fgetc() <<  0;

Esto es un poco más directa y clara acerca de lo que está tratando de lograr. También se puede implementar en un bucle para manejar números más grandes.

Usted no desea utilizar long int. Eso puede ser de diferentes tamaños en diferentes plataformas, por lo que es un no-arrancador para un formato independiente de la plataforma. Usted tiene que decidir qué rango de valores tiene que ser almacenado en el archivo. 32 bits es probablemente más fácil.

Usted dice que no está preocupado por otras plataformas ¿No . Voy a tomar eso en el sentido de que desea mantener la posibilidad de apoyarlos, en cuyo caso se debe definir el orden de bytes del formato de archivo. x86 es ascendente hacia la izquierda, por lo que se podría pensar que es lo mejor. Pero big endian es el orden de intercambio "estándar" si algo es, ya que se utiliza en la creación de redes.

Si vas para big-endian ( "orden de bytes de red"):

// can't be bothered to support really crazy platforms: it is in
// any case difficult even to exchange files with 9-bit machines,
// so we'll cross that bridge if we come to it.
assert(CHAR_BIT == 8);
assert(sizeof(uint32_t) == 4);

{
    // write value
    uint32_t value = 23;
    const uint32_t networkOrderValue = htonl(value);
    fwrite(&networkOrderValue, sizeof(uint32_t), 1, file);
}

{
    // read value
    uint32_t networkOrderValue;
    fread(&networkOrderValue, sizeof(uint32_t), 1, file);
    uint32_t value = ntohl(networkOrderValue);
}

En realidad, ni siquiera es necesario declarar dos variables, es sólo un poco confuso para reemplazar el "valor" con su equivalente orden de red en la misma variable.

Funciona porque "orden de bytes de red" se define como cualquier arreglo de bits da como resultado una orden intercambiables (big-endian) en la memoria. No hay necesidad de desorden con los sindicatos porque cualquier objeto almacenado en C puede ser tratada como una secuencia de carbón. No hay necesidad especial de los casos para endianness porque eso es lo que ntohl / htonl son para.

Si esto es demasiado lenta, se puede empezar a pensar en diabólicamente optimizado específico de la plataforma de intercambio de bytes, con SIMD o lo que sea. O el uso ascendente hacia la izquierda, en el supuesto de que la mayoría de sus plataformas será ascendente hacia la izquierda y lo que es más rápido "en promedio" a través de ellos. En ese caso, tendrá que escribir o encontrar "host a ascendente hacia la izquierda" y "ascendente hacia la izquierda para acoger" funciones, que por supuesto en x86 simplemente no hacer nada.

Creo que el enfoque de arquitectura más cruz es el uso de los tipos uintXX_t, como se define en stdint.h. Ver página del manual aquí. Por ejemplo, un int32_t le dará un número entero de 32 bits en x86 y x86-64. Puedo usar esta información por defecto ahora en toda mi código y he tenido ningún problema, ya que son bastante estándar en todos los * NIX.

Suponiendo sizeof(uint32_t) == 4, hay 4!=24 posibles órdenes de bytes, de los cuales poco-endian y big-endian son los ejemplos más prominentes, pero otros se han utilizado, así (por ejemplo PDP-endian).

Aquí son funciones para leer y escribir enteros sin signo de 32 bits de una corriente, prestando atención a un orden de bytes arbitraria que se especifica por el número entero cuya representación es la 0,1,2,3 secuencia de bytes: endian.h , endian.c

La cabecera define estos prototipos

_Bool read_uint32(uint32_t * value, FILE * file, uint32_t order);
_Bool write_uint32(uint32_t value, FILE * file, uint32_t order);

y estas constantes

LITTLE_ENDIAN
BIG_ENDIAN
PDP_ENDIAN
HOST_ORDER
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top