giovedì 21 Novembre 2024
Home / Corsi / Corso C / Lezione 7 – I file
Corso C

Lezione 7 – I file

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

  1. Soluzione esercizio proposto
  2. Come interagire con i file
  3. File di input dati
  4. File binari (lettura/scrittura)
  5. Riassunto

1 Soluzione esercizio proposto

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:

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

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.

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

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

In questa funzione, l’unica novità è la funzione strstr contenuta nell’header string.h. Se si cerca nel manuale, la sintassi è riportata come:

(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:

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):

—————————————

——————————————-

File di output

(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.

  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

 

 

 

 

 

 

 

 

 

A proposito di arkkimede

Vedi Anche

MagPi in Italiano! Gioca ai Classici Horror Retrò su Raspberry Pi 5

Estratto, tradotto in italiano, di The MagPi N°146 la rivista ufficiale della Fondazione Raspberry Pi.

Powered by themekiller.com