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
/***********************************
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:
/**********************
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
/**********************
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.
/**********************
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
/**********************
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
/**********************
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:
#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:
/**********************
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):
3.0 Frequenza [GHz] 50.0 Guadagno antenna in trasmissione [dB] 30.0 Guadagno antenna in ricezione [dB] 100.0 Potenza trasmessa [W]
—————————————
/**********************
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
# 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.
/**********************
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
RaspberryItaly Community Italiana