Eccoci qui!
Dopo tante istruzioni e costrutti, oggi vediamo come salvare i nostri dati (numerici ed alfanumerici) e come recuperarli.
Prima la soluzione dell’esercizio della lezione precedente e poi i nuovi argomenti.
Sommario
- Soluzione esercizio proposto
- Come interagire con i file
- File di input dati
- File binari (lettura/scrittura)
- Riassunto
1 Soluzione esercizio proposto
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
/*********************************** Soluzione esercizio proposto: Allocare un array bidimensionale in una subroutine e restituirlo tramite parametri di ingresso arkkimede aprile 2021 ***********************************/ #include <stdio.h> #include <stdlib.h> void *malloc_chk(size_t size, char *funcName, int line); void MakeArray(int ***m, int r, int c); int main(int argc, char *argv[]) { if ( argc != 3) { printf("uso: %s righeArrayDaAllocare colonneArrayDaAllocare\n",argv[0]); exit(1); } int righe, colonne; int **matrice; sscanf(argv[1],"%d",&righe); sscanf(argv[2],"%d",&colonne); MakeArray(&matrice, righe, colonne); for(int i=0; i<righe; i++) for(int j=0; j<colonne; j++) printf("i = %2d j = %2d matrice[i][j] = %2d\n",i,j,matrice[i][j]); } void MakeArray(int ***m, int r, int c) { int **tmpMatrice; tmpMatrice = (int **)malloc_chk(sizeof(int *)*r, __FILE__, __LINE__); for(int i=0; i<r; i++) { tmpMatrice[i] = (int *)malloc_chk(sizeof(int )*c, __FILE__, __LINE__); } for(int i=0; i<r; i++) for(int j=0; j<c; j++) tmpMatrice[i][j] = i+j; *m = tmpMatrice; return; } void *malloc_chk(size_t size, char *funcName, int line) { void *ptr; ptr = malloc(size); if(ptr == NULL) { printf("Errore nell'allocazione di memoria.\n"); printf("Quantità di meoria richiesta: %zu\n",size); printf("Sorgente dove è accaduto l'errore %s\n",funcName); printf("Lina del codice sorgente ove è accaduto l'errore: %d\n",line); printf("Esecuzione interrotta\n"); exit(11); } else return ptr; } |
Molto simile all’esercizio della lezione precedente.
2 Come interagire con i file
La prima cosa da fare è quella di dichiarare un puntatore a file tramite l’istruzione FILE *fp;
In seguito, tramite l’istruzione fopen, apriamo il file.
Vediamo un brevissimo esempio:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/********************** esempio7_1.c Come aprire un file arkkimede aprile 2021 ***********************/ #include <stdio.h> int main() { FILE *fp; fp = fopen("dati.txt","w"); fclose(fp); return 0; } |
Queste poche righe di codice, non generano scritte a video ma se le compilate ed eseguite, nella vostra directory di lavoro, troverete dati.txt, di dimensione 0: avete creato il vostro primo file!
Osserviamo meglio l’istruzione fopen:
- Necessita di una stringa che rappresenta il nome del file (da creare o da leggere). L’estensione – .dat – può essere utile ad individuarne il contenuto (.dat dati generici. .txt testo, .bin dati binari, etc)
- Necessita di una seconda stringa – non un carattere perché tra virgolette – in questo caso “w” che descrive le caratteristiche del file con il quale ci apprestiamo a lavorare. Una classificazione di questo descrittore è riportata di seguito
- “w” :Il file sarà scritto. Se già esiste un file con il medesimo nome, lo distruggerete.
- “r”: il file sarà letto. Se il file non esiste, accedendoci causerete un errore di segmentazione (segmenatation fault).
- “a”: se il file esiste, saranno aggiunti altri dati al fondo, se non esiste sarà creato.
- “r+”: apre un file per aggiornarlo sia in lettura che scrittura: il file deve esistere.
- “w+”: crea un file vuoto per lettura e scrittura
- “a+”: apre un file per leggerlo ed aggiungerli dati al fondo
- “wb”: il file sarà scritto in formato binario
- “rb”: il file sarà letto in formato binario
Proveremo ad usare alcuni di questi descrittori.
Nei primi 6 casi, per scrivere su file si usa la fprintf e per leggere si usa la fscanf.
Caso particolare sono i file binari che vedremo in seguito.
Per ora riprendiamo l’esempio7_1 e scriviamo in dati.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
/********************** esempio7_2.c Come aprire un file arkkimede aprile 2021 ***********************/ #include <stdio.h> int main() { FILE *fp; int returnValue; // Apertura del file in writing fp = fopen("dati.txt","w"); // Scrittura prima riga returnValue = fprintf(fp, "PrimaRiga\n"); printf("returnValue = %d\n",returnValue); // Scrittura seconda riga returnValue = fprintf(fp, "SecondaRiga\n"); printf("returnValue = %d\n",returnValue); // Importante chiudere il file fclose(fp); return 0; } /******************* Output: returnValue = 11 returnValue = 13 *******************/ |
Come accade per la sprintf, anche per la fprintf, il primo argomento è il puntatore al file e poi la stringa e/o il formato del dato che si vuol scrivere.
Questa funzione prevede anche un valore di ritorno intero in cui è registrato il numero di caratteri correttamente scritti nel file: questo potrebbe essere un controllo che si può testare durante l’esecuzione del programma.
Adesso il file dati.txt contiene due righe con le due stringhe PrimaRiga e SecondaRiga.
Adesso proviamo a leggere il file dati.txt.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
/********************** esempio7_3.c Come leggere un file arkkimede aprile 2021 ***********************/ #define STR_LEN 100 #include <stdio.h> #include <stdlib.h> // Prototipo funzione void *malloc_chk(size_t size, char *funcName, int line); int main() { FILE *fp; int returnValue; char *str1; char *str2; // Allochiamo due stringhe str1 = (char *)malloc_chk(sizeof(char)*STR_LEN,__FILE__,__LINE__); str2 = (char *)malloc_chk(sizeof(char)*STR_LEN,__FILE__,__LINE__); // Apertura del file in reading fp = fopen("dati.txt","r"); if ( fp == NULL ) { printf("Il file dati.txt non esiste\n"); exit(1); } // Lettura prima riga returnValue = fscanf(fp,"%s",str1 ); printf("returnValue = %d\n",returnValue); // Lettura seconda riga returnValue = fscanf(fp,"%s",str2 ); printf("returnValue = %d\n",returnValue); // Importante chiudere il file fclose(fp); printf("Prima stringa = %s \n",str1); printf("Seconda stringa = %s \n",str2); free(str1); free(str2); return 0; } void *malloc_chk(size_t size, char *funcName, int line) { void *ptr; ptr = malloc(size); if(ptr == NULL) { printf("Errore nell'allocazione di memoria.\n"); printf("Quantità di meoria richiesta: %zu\n",size); printf("Sorgente dove è accaduto l'errore %s\n",funcName); printf("Lina del codice sorgente ove è accaduto l'errore: %d\n",line); printf("Esecuzione interrotta\n"); exit(11); } else return ptr; } /******************* Output: returnValue = 1 returnValue = 1 Prima stringa = PrimaRiga Seconda stringa = SecondaRiga *******************/ |
Un paio di osservazioni sul codice riportato:
- Quando si apre un file in lettura, non è detto che il file esista, quindi è necessario verificare che il puntatore al file non sia NULL, valore restituito dalla funzione fopen se il file non esiste
- fscanf ammette come valore di uscita il numero di variabili lette correttamente (in questo caso 1).
Come ultimo esempio proviamo ad appendere al fle dati.txt dei numeri interi
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
/********************** esempio7_4.c Aprire un file in append arkkimede aprile 2021 ***********************/ #define STR_LEN 100 #include <stdio.h> #include <stdlib.h> int main() { FILE *fp; int returnValue; int num1 = 10; int num2 = 20; // Apertura del file in reading fp = fopen("dati.txt","a"); if ( fp == NULL ) { printf("Il file dati.txt non esiste\n"); exit(1); } // Scrittura primo numero returnValue = fprintf(fp,"%d\n",num1 ); printf("returnValue = %d\n",returnValue); // Scrittura secondo numero returnValue = fprintf(fp,"%d\n",num2 ); printf("returnValue = %d\n",returnValue); // Importante chiudere il file fclose(fp); return 0; } /******************* output: returnValue = 2 returnValue = 2 cat dati.txt PrimaRiga SecondaRiga 10 20 *******************/ |
Considerando i due ultimi esempi riportati, come per la malloc è possibile rimandare il controllo del puntatore in una funzione scritta per questo scopo, la medesima cosa la si può fare anche per la fopen. Di seguito riportiamo la funzione fopen_chk che controlla il puntatore restituito nell’apertura di un file
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
/********************** fopen_chk Funzione che apre un file e verifica se il puntatore sia nullo o meno. **********************/ FILE *fopen_chk(char *fileName, char *mode) { FILE *fp; char *outr, *outa, *outw; outr = strstr(mode,"r"); outa = strstr(mode,"a"); outw = strstr(mode,"w"); fp = fopen(fileName,mode); if((outr != NULL) || (outa != NULL)) { if(fp == NULL) { fprintf(stderr,"The file %s does not exist\n",fileName); fprintf(stderr,"Execution aborted 1@fopen_chk\n"); exit(7); } } else if( outw != NULL ) { if(fp == NULL) { fprintf(stderr,"Error opening the file %s\n",fileName); fprintf(stderr,"Execution aborted 2@fopen_chk\n"); exit(8); } } else { fprintf(stderr,"File open with an unknow method\n"); fprintf(stderr,"Execution aborted 3@fopen_chk\n"); exit(9); } return (fp); } |
In questa funzione, l’unica novità è la funzione strstr contenuta nell’header string.h. Se si cerca nel manuale, la sintassi è riportata come:
1 2 3 |
#include <string.h> char *strstr(char *pagliaio, char *ago); |
(ovviamente nel manuale sono in inglese). Questa funzione cerca “ago” nel “pagliaio” e se lo trova restituisce un puntatore che individua appunto l’ago. Se non lo trova, la funzione restituisce NULL.
Non mancherà occasione di utilizzarla.
Fig1 – Dati del file “nuvola.dat”
Nel repositoy di GitHub è presente nuvola.dat costituito da coppie di dati x e y, la cui rappresentazione è presente nella Fig1.
Adesso vediamo come leggere questo file e provare a calcolare il baricentro di questa nuvola ossia il valor medio delle x e delle y.
Per leggere un file dati, il problema da risolvere è quello di sapere quante righe sono presenti. Ci sono varie soluzioni a questo problema e qui se ne propone una:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
/********************** esempio7_5.c Leggere un file dati arkkimede aprile 2021 ***********************/ #define STR_LEN 100 #include <stdio.h> #include <stdlib.h> #include <string.h> // Prototipo funzione void *malloc_chk(size_t size, char *funcName, int line); FILE *fopen_chk(char *fileName, char *mode); int main() { FILE *fp; int nLinee = 0; float dummyX, dummyY; float *x, *y; // Apertura del file in reading fp = fopen_chk("nuvola.dat", "r"); // Conta delle linee presenti nel file while ( fscanf(fp,"%f %f \n",&dummyX, &dummyY) == 2) nLinee++; printf("Nel file sono presenti %d linee\n",nLinee); // Riavvolgiamo il file in modo che la prossima lettura // sia la prima riga rewind(fp); // Memoria per i dati x ed y x = (float *)malloc_chk(sizeof(float)*nLinee, __FILE__, __LINE__); y = (float *)malloc_chk(sizeof(float)*nLinee, __FILE__, __LINE__); // Loop per leggere i dati for(int i=0; i<nLinee; i++) fscanf(fp,"%f %f",&x[i],&y[i]); // Chiusura del file fclose(fp); // Calcolo dei valori medi float mediaX = 0.0; float mediaY = 0.0; for(int i=0; i<nLinee; i++) { mediaX += x[i]/nLinee; mediaY += y[i]/nLinee; } printf("La media delle ascisse : %f\n",mediaX); printf("La media delle ordinate: %f\n",mediaY); // Liberare memoria free(x); free(y); return 0; } void *malloc_chk(size_t size, char *funcName, int line) { void *ptr; ptr = malloc(size); if(ptr == NULL) { printf("Errore nell'allocazione di memoria.\n"); printf("Quantità di meoria richiesta: %zu\n",size); printf("Sorgente dove è accaduto l'errore %s\n",funcName); printf("Lina del codice sorgente ove è accaduto l'errore: %d\n",line); printf("Esecuzione interrotta\n"); exit(11); } else return ptr; } FILE *fopen_chk(char *fileName, char *mode) { FILE *fp; char *outr, *outa, *outw; outr = strstr(mode,"r"); outa = strstr(mode,"a"); outw = strstr(mode,"w"); fp = fopen(fileName,mode); if((outr != NULL) || (outa != NULL)) { if(fp == NULL) { fprintf(stderr,"The file %s does not exist\n",fileName); fprintf(stderr,"Execution aborted 1@fopen_chk\n"); exit(7); } } else if( outw != NULL ) { if(fp == NULL) { fprintf(stderr,"Error opening the file %s\n",fileName); fprintf(stderr,"Execution aborted 2@fopen_chk\n"); exit(8); } } else { fprintf(stderr,"File open with an unknow method\n"); fprintf(stderr,"Execution aborted 3@fopen_chk\n"); exit(9); } return (fp); } /********************************** Output: Nel file sono presenti 1001 linee La media delle ascisse : 5.000000 La media delle ordinate: 13.066346 *********************************** |
Alcune considerazioni
- Abbiamo utilizzato la proprietà della fscanf di restituire il numero di parametri letti per contare le linee presenti in nuvola.dat
- Il C memorizza in dati collegati ad puntatore a file, quale è stato l’ultimo dato letto. Dopo essere stato letto tutto, è necessario re-inizializzare il file in modo che la prima lettura sia la prrima riga. Per far questo si usa l’istruzione rewind(fp);
3 File di input dati
Un progetto mediamente complesso, necessita un certo numero di parametri di ingresso. E’ sicuramente sconsigliabile fare questi input da riga di comando per vari motivi:
- con il passare del tempo, i risultati ottenuti non si saprà più dire da quali parametri di ingresso derivino
- volendo vedere l’effetto di una variazione di un parametro di ingresso sul risultato finale, risulta difficile se non si hanno dati salvati
- se si modifica il sorgente e non si tiene traccia delle modifiche, i parametri di ingresso potrebbero cambiare e con il passare del tempo, dette modifiche, potrebbero essere dimenticate.
Per questo motivo, è importante ricorrere a file di input e generare file di output in cui siano presenti sia gli ingressi sia le uscite, indicando anche la data di esecuzione e la versione del sorgente.
Per questo motivo, nell’esempio7_6.c vedremo l’esempio di un file di ingresso per un semplice progetto di un ponte radio e la sua soluzione.
PROBLEMA: dati i parametri di ingresso, calcolare la potenza in Watt ricevuta da un ponte radio alla distanza di 10 chilometri (problema questo regolato dall‘equazione di Friis). Al fine di effettuare il calcolo, trasformiamo l’equazione riportata nel link in unità logaritmiche:
- dividiamo primo membro e secondo membro dell’uguaglianza per 0.001
- calcoliamo 10 il logaritmo in base 10 dei due membri ed otteniamo
- Pr_dBm = Pt_dBm + Gt_dB + Gr_dB +20Log10(lambda/(4 pi R))
- la potenza in dBm si calcola 10Log10(P/0.001) ossia è riferita ad un mW
- lambda è la lunghezza d’onda ed è uguale alla velocità_della_luce / frequenza
Esempio di file di input (input.dat):
1 2 3 4 |
3.0 Frequenza [GHz] 50.0 Guadagno antenna in trasmissione [dB] 30.0 Guadagno antenna in ricezione [dB] 100.0 Potenza trasmessa [W] |
—————————————
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
/********************** esempio7_6.c Calcolo della potenza ricevuta di un ponte radio. Equazione di Friis: Pr = Pt Gt Gr (lambda/(4*pi*R))^2 In questa relazione dividiamo primo e secondo membro per 0.001 Pr/0.001 = (Pt/0.001) Gt Gr (lambda/(4*pi*R))^2 calcoliamo il Log10 Pr_dBm = Pt_dBm + Gt_dB + Gr_dB + 20*Log10(lambda/(4*pi*R)) I guadagni sono già in decibel ma la potenza è in Watt. Bisogna prima convertire la potenza in ingresso in dBm ed ottenuto il risultato riconvertirlo in Watt. Per usare le funzioni matematiche, useremo l'header math.h Nella compilazione, è necessario fornire l'opzione relativa alla libreiria matematica (-lm). Lo script fornito nella lezione 0 già la prevede In questo header esiste la constante pi greco che è M_PI ***********************/ #define VERSION "1.0" #define STR_LEN 100 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> //Velocità della luce in m/s #define C 299792458 // Prototipo funzione // Funzione di ausilio per l'apertura dei file FILE *fopen_chk(char *fileName, char *mode); // Conversione da dBm a Watt float dBm_Watt(float dBm); // Conversione da Watt a dBm float Watt_dBm(float watt); int main(int argc, char *argv[]) { // Verifica dei parametri di ingresso if ( argc != 2 ) { printf("Uso: %s NomeFileDiInput\n",argv[0]); exit(1); } FILE *fp; // Apertura del file di input fp = fopen_chk(argv[1],"r"); // Vettore necessario nella lettura dei dati char input_buffer[STR_LEN]; // Lettura della frequenza float freq_GHz, freq_Hz; fgets(input_buffer, STR_LEN, fp); sscanf(input_buffer,"%f",&freq_GHz); freq_Hz = freq_GHz * 1.e9; // Lettura guadagno antenna tx float Gt_dB; fgets(input_buffer, STR_LEN, fp); sscanf(input_buffer,"%f",&Gt_dB); // Lettura guadagno antenna rx float Gr_dB; fgets(input_buffer, STR_LEN, fp); sscanf(input_buffer,"%f",&Gr_dB); // Lettura della potenza trasmessa float Pt, Pt_dBm; float Pr, Pr_dBm; fgets(input_buffer, STR_LEN, fp); sscanf(input_buffer,"%f",&Pt); // Chiusura del file di input fclose(fp); // Cornevrtire potenza in ingresso da Watt a dBm Pt_dBm = Watt_dBm(Pt); // R da definiziane problema è 10km float R = 10000.; // Calcolo della lunghezza d'onda float lambda; lambda = C/freq_Hz; // Calcolo della potenza ricevuta in dBm Pr_dBm = Pt_dBm + Gt_dB + Gr_dB + 20.*log10f(lambda/(4.*M_PI*R)); printf("Potenza ricevuta [dBm] = %f\n",Pr_dBm); // Conversione in Watt Pr = dBm_Watt(Pr_dBm); printf("Potenza ricevuta [mW] = %f\n",Pr*1000.); // Apertura file di output fp = fopen_chk("output.dat","w"); fprintf(fp,"# Elaborazione del programma %s versione %s del data \n",argv[0], fprintf(fp,"# Lista dei dati in ingresso:\n"); fprintf(fp,"%7.2f Frequenza [GHz]\n",freq_GHz); fprintf(fp,"%7.2f Guadagno antenna in trasmissione [dB]\n",Gt_dB); fprintf(fp,"%7.2f Guadagno antenna in ricezione [dB]\n",Gr_dB); fprintf(fp,"%7.2f Potenza trasmessa [W]\n",Pt); fprintf(fp,"# La potenza ricevuta a 10 km di distanza è:\n"); fprintf(fp,"%7.2f Potenza ricevuta [dBm]\n",Pr_dBm); fprintf(fp,"%7.2f Potenza ricevuta [mW]\n",Pr*1000.); fclose(fp); return 0; } FILE *fopen_chk(char *fileName, char *mode) { FILE *fp; char *outr, *outa, *outw; outr = strstr(mode,"r"); outa = strstr(mode,"a"); outw = strstr(mode,"w"); fp = fopen(fileName,mode); if((outr != NULL) || (outa != NULL)) { if(fp == NULL) { fprintf(stderr,"The file %s does not exist\n",fileName); fprintf(stderr,"Execution aborted 1@fopen_chk\n"); exit(7); } } else if( outw != NULL ) { if(fp == NULL) { fprintf(stderr,"Error opening the file %s\n",fileName); fprintf(stderr,"Execution aborted 2@fopen_chk\n"); exit(8); } } else { fprintf(stderr,"File open with an unknow method\n"); fprintf(stderr,"Execution aborted 3@fopen_chk\n"); exit(9); } return (fp); } /******************* ** P_dBm = 10Log10(P_Watt/0.001) ** P_Watt = 0.001 * 10^(P_dBm/10); ** *******************/ float dBm_Watt(float dBm) { float watt; watt = 0.001 * powf(10.,dBm/10.); return watt; } float Watt_dBm(float watt) { float dBm; dBm = 10. * log10f(watt/0.001); return dBm; } |
——————————————-
File di output
1 2 3 4 5 6 7 8 9 |
# Elaborazione del programma ./esempio7_6 versione 1.0 del data # Lista dei dati in ingresso: 3.00 Frequenza [GHz] 50.00 Guadagno antenna in trasmissione [dB] 30.00 Guadagno antenna in ricezione [dB] 100.00 Potenza trasmessa [W] # La potenza ricevuta a 10 km di distanza è: 8.01 Potenza ricevuta [dBm] 6.32 Potenza ricevuta [mW] |
(perdonate l’esempio banale ma non si è trovato niente di meglio): in questo modo sapremo sempre che versione del codice ha girato, quali dati di ingresso c’erano e quale è stato il risultato. Adesso alcuni commenti sul codice stesso (molti sono già presenti come commenti)
- per utilizzare le funzioni matematiche necessita math.h e quando si compila bisogna aggiungere l’opzione -lm (già presente nello script della Lezione 0)
- le funzioni matematiche utilizzate sono il logaritmo in base 10 (log10f) e la potenza (powf); la f si riferisce al fatto che hanno come argomenti float, senza f si applicano ai double
- si noti il define riferito alla versione del codice
- la funzione fgets legge tutta la riga nel vettore di caratteri usato come parametro; questo supera il problema della fscanf, che avrebbe dovuto leggere anche tutti i commenti seguenti
- nel file di output, tutte le descrizioni iniziano per #: utilizzando la fgets, controllando il primo carattere della stringa restituita si può capire se si tratta di dati o commenti
- nel file di output è stata indicata la presenza della data (intesa come giorno, mese ed anno ed eventuale ora e minuti), ma non è stata riportata perché a tal fine necessita utilizzare le strutture che vedremo prossimamente.
4 File binari
Come ultimo argomento di questa lezione vediamo i file binari.
Vari sono i motivi per cui si usano e tra questi possiamo citare la velocità con la quale si può leggere un blocco cospicuo di dati a differenza dei file in formato ASCII (Letterali). Altro motivo poi è quello di preservare il dato nella sua forma binaria e quindi non affetto da troncamenti o arrotondamenti subiti dai dati quando si scrivono con un predeterminato formato che magari, per esempio, taglia un certo numero di decimali.
La teoria afferma che per scrivere o leggere un file in formato binario, si devono usare i descrittori “wb” e “rb”. Di fatti però, il formato binario è determinato dalle istruzioni con cui si scrivono e leggono i dati. Queste sono
- fwrite(buffer,size,n,fp);
- fread(buffer,size,n,fp);
essenzialmente queste istruzioni necessitano di un vettore contenente i dati da scrivere o leggere (buffer), la dimensione di un elemento del vettore (size), il numero di elementi da salvare (n) ed il puntatore al file (fp) .
Nell’esempio7_7.c si utilizza la funzione drand48() che genera numeri double pseudo casuali tra 0 ed 1, inizializzando il seme a 100 tramite srand48(100) (per ottenere risultati ripetibili).
Di questi i primi 5 sono salvati su file in formato binario. Poi sono riletti e si verifica i dati prima e dopo averli salvati e riletti in formato binario.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
/********************** esempio7_7.c Esempio di uso di file binari arkkimede aprile 2021 ***********************/ #define STR_LEN 100 #include <stdio.h> #include <stdlib.h> #include <string.h> // Funzione di ausilio per l'apertura dei file FILE *fopen_chk(char *fileName, char *mode); // Funzione di ausilio per allocare memoria void *malloc_chk(size_t size, char *funcName, int line); int main() { // Inizializzazione del seme srand48(100); double *data; int n=5; // Riserviamo 5 posizioni per double data = (double *)malloc_chk(sizeof(double)*n,__FILE__,__LINE__); // Valorizziamo il vettore con 5 numeri casuali distribuiti tra 0 ed 1 printf("Valori dei numeri casuali prima di salvarli su file\n"); for(int i=0; i<n; i++) { data[i] = drand48(); printf("data[%d] = %lf\n",i,data[i]); } // Apriamo il file per immagazzinare questi dati in formato binario FILE *fp; // La teoria vorrebbe che qui si usasse wb ma di fatti // i dati sono binari in base a come si scrivono non a questo // descrittore fp = fopen_chk("dati.bin","w"); fwrite(data,sizeof(double),n,fp); fclose(fp); // Liberiamo la memoria precedentemente allocata free(data); // Adeso riallochiamo la memoria data = (double *)malloc_chk(sizeof(double)*n,__FILE__,__LINE__); // Riapriamo il file binario questa volta in lettura e leggiamo i dati fp = fopen_chk("dati.bin","r"); fread(data,sizeof(double),n,fp); fclose(fp); printf("****************************\n"); printf("Valori dei numeri casuali letti dal file binario\n"); for(int i=0; i<n; i++) { printf("data[%d] = %lf\n",i,data[i]); } return 0; } FILE *fopen_chk(char *fileName, char *mode) { FILE *fp; char *outr, *outa, *outw; outr = strstr(mode,"r"); outa = strstr(mode,"a"); outw = strstr(mode,"w"); fp = fopen(fileName,mode); if((outr != NULL) || (outa != NULL)) { if(fp == NULL) { fprintf(stderr,"The file %s does not exist\n",fileName); fprintf(stderr,"Execution aborted 1@fopen_chk\n"); exit(7); } } else if( outw != NULL ) { if(fp == NULL) { fprintf(stderr,"Error opening the file %s\n",fileName); fprintf(stderr,"Execution aborted 2@fopen_chk\n"); exit(8); } } else { fprintf(stderr,"File open with an unknow method\n"); fprintf(stderr,"Execution aborted 3@fopen_chk\n"); exit(9); } return (fp); } void *malloc_chk(size_t size, char *funcName, int line) { void *ptr; ptr = malloc(size); if(ptr == NULL) { printf("Errore nell'allocazione di memoria.\n"); printf("Quantità di memoria richiesta: %zu\n",size); printf("Sorgente dove è accaduto l'errore %s\n",funcName); printf("Lina del codice sorgente ove è accaduto l'errore: %d\n",line); printf("Esecuzione interrotta\n"); exit(11); } else return ptr; } /************************** Output: Valori dei numeri casuali prima di salvarli su file data[0] = 0.251059 data[1] = 0.208948 data[2] = 0.940928 data[3] = 0.422546 data[4] = 0.395893 **************************** Valori dei numeri casuali letti dal file binario data[0] = 0.251059 data[1] = 0.208948 data[2] = 0.940928 data[3] = 0.422546 data[4] = 0.395893 ****************************/ |
Si provi a vedere la dimensione del file dati.bin, questa sarà esattamente 40: sono stati salvati 5 double che sono lunghi 8 byte quindi esattamente 5 x 8 = 40.
5 Riassunto
- Abbiamo visto i file, come aprirli, scriverli e chiuderli
- Abbiamo visto sia i file alfanumerici e quelli binari
- Per scrivere/leggere i primi si usano le istruzioni fprintf/fscanf
- Se si vuole leggere un’intera riga di un file si usa la funzione fgets
- Per i file binari si usano le istruzioni fwrite/fread
- E’ stata vista una tecnica per leggere i file dati
- E’ stata suggerita una strategia per gestire gli input e gli output
- Infine è stata proposta una funzione per interagire correttamente con i file
Finisce qui questa lunga lezione sui file.
Qui il forum di supporto al corso.
Se vuoi restare aggiornato, seguici anche sui nostri social: Facebook, Twitter, Youtube
Se vuoi anche trovare prodotti e accessori Raspberry Pi in offerta, seguici anche su Telegram !!
ATTENZIONE La prossima lezione verrà pubblicata non il 18 ma il 25 Maggio. Arrivederci