2015-06-24 5 views
5

Sto progettando un formato di file binario per memorizzare stringhe [senza terminare null per risparmiare spazio] e dati binari.Come gestire i problemi di portabilità in un formato di file binario

i. Qual è il modo migliore per gestire sistemi little/big endian? i.a Converte tutto in ordine di byte di rete e viceversa con ntohl()/htonl() funziona?

ii. Le strutture impacchettate avranno le stesse dimensioni su x86, x64 e arm?

iii. Sono la loro debolezza intrinseca con questo approccio?

struct __attribute__((packed)) Header { 
    uint8_t magic; 
    uint8_t flags; 
}; 

struct __attribute__((packed)) Record { 
    uint64_t length; 
    uint32_t crc; 
    uint16_t year; 
    uint8_t day; 
    uint8_t month; 
    uint8_t hour; 
    uint8_t minute; 
    uint8_t second; 
    uint8_t type; 
}; 

codice Tester Sto utilizzando il sviluppano il formato:

#include <stdlib.h> 
#include <unistd.h> 
#include <stdio.h> 
#include <limits.h> 
#include <strings.h> 
#include <stdint.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <string.h> 

struct __attribute__((packed)) Header { 
    uint8_t magic; 
    uint8_t flags; 
}; 

struct __attribute__((packed)) Record { 
    uint64_t length; 
    uint32_t crc; 
    uint16_t year; 
    uint8_t day; 
    uint8_t month; 
    uint8_t hour; 
    uint8_t minute; 
    uint8_t second; 
    uint8_t type; 
}; 

    int main(void) 
    { 

     int fd = open("test.dat", O_RDWR|O_APPEND|O_CREAT, 444); 
     struct Header header = {1, 0}; 
     write(fd, &header, sizeof(header)); 
     char msg[] = {"BINARY"}; 
     struct Record record = {strlen(msg), 0, 0, 0, 0, 0, 0, 0}; 
     write(fd, &record, sizeof(record)); 
     write(fd, msg, record.length); 
     close(fd); 
     fd = open("test.dat", O_RDWR|O_APPEND|O_CREAT, 444); 


     read(fd, &header, sizeof(struct Header)); 
     read(fd, &record, sizeof(struct Record)); 
     int len = record.length; 
     char c; 
     while (len != 0) { 
      read(fd, &c, 1); 
      len--; 
      printf("%c", c); 
     } 
     close(fd); 
    } 
+0

Sto votando per chiudere, come troppo generico per SO - scusa, prova un altro sito! – Olaf

+3

@Olaf: vorrei votare per mantenere: questo è un tipo di domanda molto pratico nel mondo reale che viene fuori tutto il tempo. Solo perché non ha una singola risposta tagliata e secca non significa che non meriti considerazione. (Con quello detto, però, non sono un SO regolare, quindi se il consenso è che ci sono alcune porzioni di domande di programmazione pratiche e reali che questo sito non è per, non sono in grado di discutere.) –

+0

@SteveSummit: Sono d'accordo sul fatto che la domanda è in realtà interessante (attenzione al mio "scusa"). Tuttavia, è fuori tema per SO. Spero davvero che l'OP trovi un altro sito (non è sicuro, se ce n'è uno nello scambio di stack). Per il voto: bene, questo è chiaramente il mio parere. Se gli altri pensano diversamente, resterà aperto. Posso vivere con quello. – Olaf

risposta

7

i. Definire il file in un ordine e convertirlo in e dall'ordine "interno", se necessario, durante la lettura/scrittura (forse con ntohl e simili) è, secondo me, l'approccio migliore.

ii. Non mi fido delle strutture imballate. Potrebbero funzionare per questo approccio per quelle piattaforme, ma non ci sono garanzie.

iii. Leggere e scrivere file binari usando fread e fwrite su intere strutture è (sempre secondo me) un approccio intrinsecamente debole. Massimizza la probabilità che ti vengano morsi problemi di dimensioni di parole, problemi di riempimento e allineamento e problemi di ordine dei byte.

Quello che mi piace fare è scrivere piccole funzioni come get16() e put32() che leggono e scrivono un byte alla volta e quindi sono intrinsecamente insensibili alle dimensioni della parola e alle difficoltà nell'ordine dei byte. Quindi scrivo direttamente le funzioni putHeader e getRecord (e simili) in termini di questi.

unsigned int get16(FILE *fp) 
{ 
    unsigned int r; 
    r = getc(fp); 
    r = (r << 8) | getc(fp); 
    return r; 
} 

void put32(unsigned long int x, FILE *fp) 
{ 
    putc((int)((x >> 24) & 0xff), fp); 
    putc((int)((x >> 16) & 0xff), fp); 
    putc((int)((x >> 8) & 0xff), fp); 
    putc((int)(x & 0xff), fp); 
} 

[P.S. Come @Olaf indica correttamente in uno dei commenti, nel codice di produzione è necessario gestire l'EOF e l'errore in queste funzioni. Li ho lasciati fuori per semplicità di presentazione.]

+0

Implementerebbe get16() con l'aritmetica del puntatore su un buffer mmap ed? – clockley1

+0

Sicuro. L'ho fatto anch'io. –

+0

Vedi? In realtà _did_ vote ;-) Nota @ user1450181: devi aggiungere definitivamente la gestione degli errori, rilevando 'EOF'/errore per le indicazioni di buth! Inoltre entrambe le note: 'getc()' restituisce 'int'. la trasmissione a 'unsigned' è implementazione definita_ per valori negativi. – Olaf

Problemi correlati