[C] Salvare una struct su file
Mi ritrovo a dover risolvere questo problema per un progetto in C, devo gestirmi una serie di array di struct (Nello specifico ciascuna contiene dati relativi ad una componente per pc tipo cpu, mobo etc) e salvare gli array di cui sopra su file.
La mia domanda e':
Uso fprintf o fwrite per salvarle? Un amico mi ha consigliato fwrite e salvare tutta la struct, ma sinceramente non ho capito bene la cosa e non ho potuto indagare oltre con lui per mancanza di tempo.
Se faccio fwrite ed inserisco tra i parametri semplicemente il nome della struct, es "nome_struct", mi copia tutti i sottocampi sul file?
Inoltre se e' possibile segnalarmi una buona guida sulla gestione del file system in C perche' ammetto di esser carente in materia.
Grazie in anticipo per l'aiuto e l'attenzione.
La mia domanda e':
Uso fprintf o fwrite per salvarle? Un amico mi ha consigliato fwrite e salvare tutta la struct, ma sinceramente non ho capito bene la cosa e non ho potuto indagare oltre con lui per mancanza di tempo.
Se faccio fwrite ed inserisco tra i parametri semplicemente il nome della struct, es "nome_struct", mi copia tutti i sottocampi sul file?
Inoltre se e' possibile segnalarmi una buona guida sulla gestione del file system in C perche' ammetto di esser carente in materia.
Grazie in anticipo per l'aiuto e l'attenzione.
Risposte
*update*
Sto' cercando di impratichirmi con le funzioni fread e fwrite, ho scritto due programmini, uno che scrive una struct di 2 interi su un file:
ed uno che tecnicamente dovrebbe leggere il file in questione:
Il problema e' che mi da' errore per il sizeof che da quel che sto' capendo dovrebbe contenere il tipo di variabile che voglio analizzare.
Qualcuno ha idea di dove sbaglio?
Sto' cercando di impratichirmi con le funzioni fread e fwrite, ho scritto due programmini, uno che scrive una struct di 2 interi su un file:
#include <stdio.h> typedef struct { int a; int b;} lista_prova; lista_prova lista; main () { FILE *pf; printf ("Inserire a:\n"); scanf ("%d", &lista.a); printf ("Inserire b:\n"); scanf ("%d", &lista.b); i++; pf = fopen ("prova.bin", "wb"); fwrite (lista, 1, sizeof(lista), pf); fclose (pf); system ("pause"); }
ed uno che tecnicamente dovrebbe leggere il file in questione:
#include <stdio.h> typedef struct { int a; int b;} lista_prova; lista_prova lista; main () { FILE *pf; pf = fopen ("prova.bin", "rb"); fread (lista, sizeof(lista_prova), 1, pf); //Dovrebbe farmi leggere un elemento di lista dal mio file, giusto? printf ("%d/n", lista.a); printf ("%d/n", lista.b); fclose(pf); system ("pause"); }
Il problema e' che mi da' errore per il sizeof che da quel che sto' capendo dovrebbe contenere il tipo di variabile che voglio analizzare.
Qualcuno ha idea di dove sbaglio?
Se non ricordo male system (la cui utilità è molto dubbia in ogni caso) è definito in stdlib.h, che non hai incluso. Inoltre non vedo come il problema possa essere sizeof. A mio parere il problema è che fread e fwrite richiedono come primo argomento un puntatore a qualcosa e stai passando una struttura. Dovresti scrivere &lista al posto di lista. La variabile i nel primo codice non è inoltre stata dichiarata. Hai infine scritto /n al posto di \n quando stampi i valori nel secondo codice. Il seguente codice, che unisce i tuoi due codici, dovrebbe essere corretto.
#include <stdio.h> typedef struct { int a; int b; } lista_prova; lista_prova lista; int main(void) { FILE *pf; printf("Inserire a:\n"); scanf("%d", &lista.a); printf("Inserire b:\n"); scanf("%d", &lista.b); pf = fopen("prova.bin", "wb"); fwrite(&lista, sizeof(lista), 1, pf); fclose(pf); pf = fopen("prova.bin", "rb"); fread(&lista, sizeof(lista), 1, pf); //Dovrebbe farmi leggere un elemento di lista dal mio file, giusto? printf("%d\n", lista.a); printf("%d\n", lista.b); fclose(pf); return 0; }
Vero, ho solo di che vergognarmi di quello che ho scritto.
Adesso sto' cercando di usare fread e fwrite per leggere e scrivere struct su file. Al momento credo di riuscire a scrivere (Ovviamente con un file.bin non posso controllare) mentre invece quando leggo il file ho un po' di problemi.
Cerco di spiegarmi:
Dovrebbe leggermi due coppie di interi. Il problema e' che mi da' in uscita:
0
0
n1
n2
Adesso sto' cercando di usare fread e fwrite per leggere e scrivere struct su file. Al momento credo di riuscire a scrivere (Ovviamente con un file.bin non posso controllare) mentre invece quando leggo il file ho un po' di problemi.
Cerco di spiegarmi:
#include <stdio.h> #include <stdlib.h> typedef struct { int a; int b;} lista_prova; lista_prova lista; int i = 0; main () { FILE *pf; pf = fopen ("prova.bin", "rb"); fread (&lista, sizeof(lista_prova), 2, pf); fclose(pf); while (i < 2) { printf ("%d\n", lista.a); printf ("%d\n", lista.b); i++; } system ("pause"); }
Dovrebbe leggermi due coppie di interi. Il problema e' che mi da' in uscita:
0
0
n1
n2
Ma lista non è un array di lista_prova.. Può contenere soltanto una singola copia della struttura lista. Non sapendo che cosa ci hai scritto nel file e cosa vuoi ottenere non saprei come interpretare il risultato ottenuto.
Questo e' il file per inserire le struct:
Le intenzioni erano:
Creare un file "prova.bin" vuoto, dopodiche' aprirlo in modalita' append. Inserire due coppie di interi a e b di modo che rimanessero salvate sul file.
A quel punto col precedente programma listavo i le due coppie.
(Il problemaccio e' che il prof c'ha detto "cavatevela da soli" in buona sostanza, quindi sto' cercando sulla rete qualche guida esauriente o aiuti)
*edit*
Modificato cosi' il file funziona, quindi se ho capito bene:
Con fwrite salvo la struct, usando la modalita' append creo in pratica una struct.
Poi col fread devo copiarmi la struct in un generico array e ci lavoro sopra.
Esatto?
#include <stdio.h> #include <stdlib.h> typedef struct { int a; int b;} lista_prova; typedef lista_prova lista_array[2]; lista_array lista; int i = 0; main () { FILE *pf; pf = fopen ("prova.bin", "rb"); while (i < 2) { fread (&lista, sizeof(lista_array), 1, pf); fclose(pf); printf ("%d\n", lista[i].a); printf ("%d\n", lista[i].b); i++; } system ("pause"); }
Le intenzioni erano:
Creare un file "prova.bin" vuoto, dopodiche' aprirlo in modalita' append. Inserire due coppie di interi a e b di modo che rimanessero salvate sul file.
A quel punto col precedente programma listavo i le due coppie.
(Il problemaccio e' che il prof c'ha detto "cavatevela da soli" in buona sostanza, quindi sto' cercando sulla rete qualche guida esauriente o aiuti)
*edit*
Modificato cosi' il file funziona, quindi se ho capito bene:
Con fwrite salvo la struct, usando la modalita' append creo in pratica una struct.
Poi col fread devo copiarmi la struct in un generico array e ci lavoro sopra.
Esatto?
Si qualcosa del genere. La fread la devi però fare sui singoli elementi dell'array. Così sovrascrivi il primo.
#include <stdio.h> typedef struct { int a; int b;} lista_prova; lista_prova lista; main () { FILE *pf; int i = 0; pf = fopen ("prova.bin", "wb"); fclose (pf); pf = fopen ("prova.bin", "ab+"); while (i < 2) { printf ("Inserire a:\n"); scanf ("%d", &lista.a); printf ("Inserire b:\n"); scanf ("%d", &lista.b); fwrite (&lista, sizeof(lista_prova), 1, pf); i++; } fclose (pf); system ("pause"); }
Programma per inserire le due coppie.
#include <stdio.h> #include <stdlib.h> typedef struct { int a; int b;} lista_prova; typedef lista_prova lista_array[2]; lista_array lista; int i = 0; main () { FILE *pf; pf = fopen ("prova.bin", "rb"); while (i < 2) { fread (&lista, sizeof(lista_array), 1, pf); fclose(pf); printf ("%d\n", lista[i].a); printf ("%d\n", lista[i].b); i++; } system ("pause"); }
Programma per leggere e stampare a video.
Questi vanno, adesso per iniziare a lavorare sul progetto originale:
In scrittura le struct le infilo una ad una, modalita' append. In questa maniera il file.bin mi diventa un array di suo. La struct non ha bisogno di essere un array.
In lettura leggo una alla volta dal file e copio su un array di struct in memoria di modo da poterlo poi gestire come un normalissimo array.
Se voglio cancellare un elemento dal file potrei praticamente cancellarlo dall'array in memoria e poi sovrascrivere l'array modificato sul file giusto? Ogni volta che apro il file in modalita' "wb" cancello quello che c'era scritto precedentemente?
Grazie ancora per la pazienza.
pf = fopen ("prova.bin", "wb"); fclose (pf); pf = fopen ("prova.bin", "ab+");
Per prima cosa, quando il file non esiste, aprendo il file con append causa la creazione del file. Non è necessario aprire il file due volte come hai fatto. C'è poi una ragione per aprire il file con '+'? A me sembra che tu stia solo scrivendo e che non sia quindi necessario aprire il file in mixed mode (sia in scrittura che lettura insomma). Ma forse ha senso nell'applicazione finale.
In lettura leggo una alla volta dal file e copio su un array di struct in memoria di modo da poterlo poi gestire come un normalissimo array.
Ci sono in realtà due modi per evitare il dover caricare un elemento dell'array per volta (essendo in qualche modo forse anche costretti a gestire un qualche array dinamico):
1. Inserire la dimensione dell'array nel file nei primi sizeof(int) bytes. In questo modo puoi caricare i primi bytes del file per ottenere la dimensione dell'array e poi usare una singola fread per caricare tutto l'array.
2. Usare fseek e ftell per ottenere la dimensione del file e quindi caricarlo interamente in memoria in un array di strutture grande abbastanza.
Per cancellare un elemento dall'array devi aggiornare l'array e riscrivere interamente il file. Aprendo con "wb" il contenuto del file viene cancellato.
"apatriarca":pf = fopen ("prova.bin", "wb"); fclose (pf); pf = fopen ("prova.bin", "ab+");
Per prima cosa, quando il file non esiste, aprendo il file con append causa la creazione del file. Non è necessario aprire il file due volte come hai fatto. C'è poi una ragione per aprire il file con '+'? A me sembra che tu stia solo scrivendo e che non sia quindi necessario aprire il file in mixed mode (sia in scrittura che lettura insomma). Ma forse ha senso nell'applicazione finale.
Non so' se aveva senso, ma l'idea (probabilmente sbagliatissima) era di salvare sul file sia l'array che una variabile di tipo int che tenesse conto della dimensione dello stesso. Praticamente ho scoperto che non mi serve e quindi adesso nella nuova versione dell'applicazione non la uso.
Domanda legata alla questione:
Se faccio fopen in modalita' "wb" di un file gia' esistente, ne cancello il contenuto?
"apatriarca":
Per cancellare un elemento dall'array devi aggiornare l'array e riscrivere interamente il file. Aprendo con "wb" il contenuto del file viene cancellato.
Update:
Ho appena finito questa funzione per inserire una struct sul file:
Non capisco perche' ma non mi entra mai nella funzione per inserire i dati.
Dovrebbe entrarmi nel ciclo "if j < limit" ma non lo fa'.
PS: so' che e' un po' "sporca" devo ancora rifinire e togliere le parti inutili.
Ho appena finito questa funzione per inserire una struct sul file:
void insertcpu (dati_cpu cpulist) { system ("cls"); int tempcpu, tempcore, tempcache, tempwatt, tempprezzo; char tempmodello[10]; int i = 0; char tempcmpcpu[10]; int cmp, finalcmp; system ("cls"); printf ("Inserire modello CPU:\n"); scanf ("%s", &tempmodello); pf = fopen ("filecpu.bin", "rb"); fread (&cpulist, sizeof(dati_cpu), 1, pf); while ( !feof(pf) ) { strcpy (tempcpulist[i].modello, cpulist.modello); i++; fread (&cpulist, sizeof(dati_cpu), 1, pf); } fclose (pf); int j; j = 0; int limit; limit = i; while (j < limit) { strcpy (tempcmpcpu, tempcpulist[j].modello); cmp = strcmp(tempmodello, tempcmpcpu); if (cmp == 0) break; else printf ("%d", cmp); system ("pause"); j++; } <--- Questa e' la parte che non mi si attiva. if (j < limit) { strcpy (cpulist.modello, tempmodello); system ("cls"); printf ("Inserire produttore CPU:\n1 - Intel\n2 - AMD\n"); scanf ("%d", &tempcpu); while ((tempcpu < 1) || (tempcpu > 2)) { printf ("Inserire un produttore valido:\n1 - Intel\n2 - AMD\n"); scanf ("%d", &tempcpu); } cpulist.produttore = tempcpu; system ("cls"); printf ("Inserire numero di Core\n1 - SingleCore\n2 - DualCore\n4 - QuadCore\n"); scanf ("%d", &tempcore); while ((tempcore != 1) && (tempcore != 2) && (tempcore != 4)) { printf ("Un processore puo' essere solo SingleCore, DualCore o QuadCore\n"); scanf ("%d", &tempcore); } cpulist.core = tempcore; system ("cls"); printf ("Inserire valore della Cache Principale\n"); scanf ("%d", &tempcache); while ((tempcache != 2) && (tempcache != 4)) { printf ("Un Processore puo' avere 2 o 4 mb di Cache\n"); scanf ("%d", &tempcache); } cpulist.cache = tempcache; system ("cls"); printf ("Inserire il consumo in Watt\n"); scanf ("%d", &tempwatt); while ((tempwatt < 50) || (tempwatt > 150)) { printf ("I processori consumano tra i 50 ed i 150 Watt\n"); scanf ("%d", &tempwatt); } cpulist.watt = tempwatt; system ("cls"); printf ("Inserire il prezzo in Euro\n"); scanf ("%d", &cpulist.prezzo); pf = fopen ("filecpu.bin", "ab"); fwrite (&cpulist, sizeof(dati_cpu), 1, pf); fclose (pf); } else printf ("Esiste gia' una CPU nella lista/n"); system ("pause"); // Termine insertcpu }
Non capisco perche' ma non mi entra mai nella funzione per inserire i dati.
Dovrebbe entrarmi nel ciclo "if j < limit" ma non lo fa'.
PS: so' che e' un po' "sporca" devo ancora rifinire e togliere le parti inutili.
Il ciclo precedente ha come condizioni di uscita j >= limit. Per cui, una volta usciti dal ciclo, non potrà essere verificata la condizione j < limit...
Io sono un pollo.
Provo ad aggiustarlo, grazie ancora infinite.
*edit*
Continua a non funzionarmi, suggerimenti per la soluzione?
Provo ad aggiustarlo, grazie ancora infinite.
*edit*
Continua a non funzionarmi, suggerimenti per la soluzione?
void insertcpu (dati_cpu cpulist) { char tempmodello[10]; char compmodello[10]; int cpucontrol = 0; int i = 0; int limit, limitloop, j, cpucompare, tempproduttore, cpuava; system ("cls"); pf = fopen ("cpusave.bin", "rb"); fread (&cpulist, sizeof(dati_cpu), 1, pf); while ( !feof(pf) ) { strcpy (tempcpulist[i].modello, cpulist.modello); fread (&cpulist, sizeof(dati_cpu), 1, pf); i++; } fclose (pf); limit = i; printf ("Presenti in lista %d elementi\n", i); system ("pause"); if (limit == 5) { printf ("La lista e' piena\n"); system ("pause"); } printf ("Inserire nuova CPU\n"); scanf ("%s", &tempmodello); j = 0; cpuava = 0; cpucompare = strcmp(tempmodello, tempcpulist[j].modello); printf ("%s\n", tempcpulist[j].modello); while (cpucompare != 0) { j++; cpucompare = strcmp(tempmodello, tempcpulist[j].modello); printf ("%s\n", tempcpulist[j].modello); if (cpucompare != 0) break; else if (j > limit) { cpuava = 1; break; } } printf ("%d\n", j); if (cpuava == 1) { strcpy (cpulist.modello, tempmodello); system ("cls"); printf ("Inserire il produttore:\n1 - Intel\n2 - AMD\n"); scanf ("%d", &tempproduttore); while ((tempproduttore < 1) || (tempproduttore > 2)) { printf ("Inserire un produttore valido\n"); scanf ("%d", &tempproduttore); } cpulist.produttore = tempproduttore; pf = fopen("cpusave.bin", "ab"); fwrite (&cpulist, sizeof(dati_cpu), 1, pf); fclose(pf); } else printf ("gia' presente\n"); system ("pause"); // Termine insertcpu }
Fatta questa funzione, funziona per i primi due, ma quando devo inserire il terzo mi dice che gia' e' presente in ogni caso.
Sono sicuro che sia un errore di logica ma non riesco a trovarlo.