2010-07-18 6 views
7

Sto tentando di salvare una struttura con una stringa char * in un file.Salvataggio di una struttura C con una stringa char * in un file

struct d_object { 
    int flags; 
    int time; 
    int offset; 
    char *filename; 
}; 

Il problema è che quando lo faccio, ovviamente, salverò solo l'indirizzo di quel puntatore anziché la stringa. Quindi quello che ho fatto è semplicemente usare un array di caratteri e sono costretto a impostare la dimensione massima della stringa. Funziona bene, tuttavia mi stavo chiedendo se è comunque possibile memorizzare la struttura con un char * (che io malloc ad un certo punto) in un file e quindi recuperarlo. Posso salvare la stringa e la struct separate e poi recuperarle ma è un bel casino. Sarebbe preferibile caricare e salvare l'intera struttura (quella sopra) nel file. Grazie!

Il codice con l'array di caratteri è qui sotto:

#include <stdio.h> 
#include <string.h> 
#include <fcntl.h> 

struct d_object { 
    int flags; 
    int time; 
    int offset; 
    char filename[255]; 
}; 

int main(int argc, char **argv) { 

    struct d_object fcb; 

    fcb.flags=5; 
    fcb.time=100000; 
    fcb.offset=220; 
    strncpy(fcb.filename,"myfile",255); 


    int fd=open("testfile",O_RDWR); 
    write(fd,&fcb,sizeof(fcb)); 
    close(fd); 


    int fd2 = open("testfile",O_RDONLY); 
    struct d_object new_fcb; 
    read(fd2,&new_fcb,sizeof(new_fcb)); 

    printf("read from file testfile: %s\n",new_fcb.filename); 

    return 0; 

} 

P.S .: Non sto utilizzando le funzioni di flusso, semplicemente perché questo è in realtà pensato per essere eseguito su un sistema operativo embedded che non li hanno. Ho appena adattato il codice per * BSD/Linux, quindi ha più senso quando si pone la domanda.

+0

Il modo in cui stai usando fread è molto sbagliato. Non si dichiara un buffer di caratteri e non si legge in esso, quindi puntare un puntatore di struct su di esso. Basta dichiarare una struct e passare il puntatore a quella struttura da battere. Il modo in cui lo hai fatto non è valido C ed è probabile che si blocchi sulle architetture RISC con requisiti di allineamento rigorosi. –

+1

ok, grazie per quello. Tuttavia questo non è il problema principale al momento. – theprole

risposta

7

Capisco che la portabilità non è un problema, dal momento che si sta lavorando per un sistema embedded. In altri casi, dovresti usare qualcosa come XML.

È possibile trasformare di nuovo il codice per:

struct d_object { 
    int flags; 
    int time; 
    int offset; 
    char * filename; 
}; 

E poi salvare ogni pezzo di dati singolarmente:

write(fd, &record.flags, sizeof(int)); 
write(fd, &record.time, sizeof(int)); 
write(fd, &record.offset, sizeof(int)); 
int filename_length = strlen(filename); 
write(fd, &filename_length, sizeof(int)); 
write(fd, record.filename, filename_length); 

Per la lettura, si dovrà leggere ogni elemento separatedly, e poi il nome file:

+0

grazie, gli ultimi tre commenti hanno aiutato con la risposta – theprole

2

La serializzazione non è mai carina. Che ne dici di memorizzare la lunghezza della stringa nel puntatore e lasciare che la stringa segua la struttura nel file? Qualcosa di simile (attenzione, il codice del cervello-compilato):

void write_object(struct d_object *s, int fd) { 
    struct d_object copy = *s; 
    copy.filename = (char*)strlen(s->filename); 
    write(fd, &copy, sizeof(copy)); 
    write(fd, s->filename, (size_t)copy.filename); 
} 

void read_object(struct d_object *s, int fd) { 
    read(fd, s, sizeof(struct d_object)); 
    char *filename = malloc(((size_t)s->filename) + 1); 
    read(fd, filename, (size_t)s->filename); 
    filename[(size_t)s->filename] = '\0'; 
    s->filename = filename; 
} 
+1

Non sono completamente d'accordo nel mettere la lunghezza del nome in un 'char *'. Questa è una soluzione pragmatica e funziona, ma è confusa e considerata "cattivo stile". Potrebbe persino esplodere se 'int' mai risultasse più grande di' char * '. –

+0

@Carl, per quanto sia d'accordo in linea di principio, mentre 'int' potrebbe in teoria essere maggiore di' char * ', il valore di ritorno di' strlen' non potrebbe mai essere maggiore di 'char *' da un semplice argomento di conteggio. :-) Detto questo, anziché utilizzare il cast, vorrei usare '(char *) 0 + strlen (s-

1

No, non c'è nessuna magia integrato direttamente nella lingua di fare questo per voi.

Quello che devi fare è scrivere un paio di funzioni per convertire i tuoi dati in forma scrivibile e leggerli di nuovo dal file. Questo è chiamato "serializzazione"/"deserializzazione" in lingue che fanno più storie su di esso.

La tua particolare struttura, potresti fare qualcosa come scrivere i file binari nel file direttamente dalla struct, e poi seguirlo con il contenuto del buffer dei caratteri. Potresti rendere le cose più semplici per te stesso quando leggi i dati del personaggio con un int specificando la sua lunghezza.

Quando leggi di nuovo quella roba, ti consigliamo di scrivere malloc/calloc una porzione di memoria per contenere i dati di char; se hai memorizzato la dimensione, saprai quanto è grande da fare malloc. Leggi i dati binari nella struct, leggi i dati char nella memoria malloc'd, memorizza il puntatore nel blocco malloc nella struct e hai ricreato l'originale.

Nessuna magia. Solo dati e un po 'di grasso di gomito.

EDIT

Mentre ho scritto su questa operazione, Thomas codificato un esempio. Penso che le nostre risposte si completino a vicenda molto bene; insieme dovrebbero dirti tutto ciò che devi sapere.

+0

grazie, gli ultimi tre commenti hanno aiutato con la risposta – theprole

2

Il tuo problema qui è davvero un sintomo di un problema molto più grande: non dovresti leggere/scrivere strutture di dati binari tra memoria e file. Non solo non esiste un modo chiaro di leggere/scrivere strutture con puntatori ad altri dati (questo vale non solo per le stringhe ma per dati annidati, elenchi concatenati, ecc.) Ma il formato dei dati sul disco dipenderà dal computer host e Implementazione C e non sarà trasferibile ad altri ambienti.

Invece, è necessario progettare un formato per i dati su disco e scrivere funzioni per salvare e caricare i dati, oppure utilizzare un formato esistente e trovare il codice libreria per utilizzarlo. Questo è solitamente definito come "serializzazione".

Se i dati sono gerarchici, JSON, XML o EBML possono essere appropriati. Se è abbastanza semplice, i file di testo flat o un formato binario homebrew (scritto byte per byte in modo che sia portatile) possono essere appropriati.

Poiché sembra che tu non abbia familiarità con questi problemi, potrebbe essere utile scrivere del codice per caricare/salvare alcuni semplici formati di file binari (come .tga o .wav) come esercizio prima di provare a progettare qualcosa per i tuoi dati

+0

So che non sarà portatile e va bene perché non ha bisogno di essere. Questo codice è in esecuzione su un sistema operativo integrato (contiki) con 10k di RAM! Come puoi immaginare, nessuna delle tecnologie che hai menzionato funzionerà da remoto, per non parlare del supporto in questo piccolo sistema operativo. Avrei dovuto chiarirlo nella domanda. Grazie comunque per i suggerimenti. Sto ancora cercando di vedere come serializzare i dati – theprole

+0

un file di testo potrebbe essere la soluzione migliore che penso.O se la portabilità non è un problema, penseresti che sia corretto scrivere binari dritti nel file? – theprole

2

Ora che conosco la natura del problema, perché non provare la flessibilità array? Invece di utilizzare char *filename;, utilizzare char filename[1] e malloc(sizeof struct d_object + filename_len) per allocare le strutture. Aggiungi un membro della dimensione e puoi facilmente scrivere l'oggetto su disco con una singola chiamata a write e caricarlo da disco con 2 chiamate (prima leggere l'elemento dimensione, poi leggere l'intero oggetto dopo averlo assegnato).

Nota che il modo "ufficiale" per fare le matrici flessibili in C99 è [] piuttosto che [1], ma [1] è garantito per funzionare come conseguenza di altri requisiti indicati nella norma e funziona su C89 troppo. [1] però sprecherà pochi byte, quindi se il compilatore supporta [] potresti volerlo usare.

Problemi correlati