Lettura e scrittura formattata su File
Prof. Cesario Vincenzo Angelino
1
File
La memorizzazione dei dati nelle variabili di un programma è
temporanea: tali dati vano perduti al termine dell’esecuzione
del programma.
Per poter mantenere disponibili i dati tra le diverse esecuzioni
di un programma è necessario rendere persistenti i dati.
A tale scopo i dati devono essere archiviati su memoria di
massa (dischi, cd).
I file sono un’astrazione per la gestione della persistenza dei
dati.
File
Un file è una astrazione fornita dal sistema operativo, il cui
scopo è consentire la memorizzazione di informazioni su
memoria di massa.
Concettualmente, un file è una sequenza di registrazioni
uniformi, cioè dello stesso tipo.
Un file è un’astrazione di memorizzazione di dimensione
potenzialmente illimitata (ma non infinita) ad accesso
sequenziale.
A livello di sistema operativo un file è denotato univocamente
dal suo nome assoluto, che comprende il percorso e il nome
relativo.
File
• Unità di memorizzazione dati da parte del FileSystem
• Contiene informazioni conservate in maniera non volatile
nelle memorie di massa
• Dati sono salvati come byte
Tipologia:
o Testo: Ogni byte è la rappresentazione ASCII di un carattere
o Binari: Ogni byte è la rappresentazione dell'informazione
specifica dell'applicazione (Word, Excel, ….)
File in C
Il C offre soltanto alcune funzioni di base per operare sui
file:
• non esiste il concetto di record e di chiave di ricerca;
• non esistono pertanto funzioni di inserimento,
modifica, cancellazione e ricerca di record all’interno di
un file.
È consentito operare solamente sui file costituiti da
sequenze lineari di byte/testo, mediante funzioni per la
creazione, rimozione di file, lettura e scrittura di byte/testo
da/su file.
File in C
Il sistema di I/O C associa ad ogni dispositivo fisico un dispositivo
logico chiamato flusso (stream).
Uno stream rappresenta il canale di comunicazione tra il programma
ed il dispositivo fisico
Programma Flusso Dispositivo
Fisico
Poiché tutti i flussi si comportano alla stessa maniera, possono essere
utilizzate le stesse funzioni per la loro gestione.
File in C
Esistono due tipi di flussi: flussi binari e di flussi di testo.
• Un flusso binario è formato da una sequenza di byte con una
corrispondenza uno ad uno con i byte presenti sul dispositivo fisico.
I dati sono gestiti esattamente nel modo in cui sono rappresentati
in memoria, senza traduzione di caratteri.
• Un flusso di testo è una sequenza di caratteri generalmente
suddivisa in linee terminate da un carattere di newline (‘\n’). I dati
vengono gestiti usando la loro rappresentazione in forma di
sequenza di caratteri.
• Ad esempio, in un flusso di testo, un dato di tipo int viene
specificato tramite una sequenza di caratteri la cui lunghezza
dipende dallo specifico dato. In un flusso binario, differenti dati di
tipo int vengono rappresentati sempre con lo stesso numero di byte
pari a sizeof(int).
File in C
Sono tre le operazioni per gestire i flussi in un programma:
• Apertura di un flusso: per associare un flusso ad un dispositivo
fisico (ad esempio, un file su disco fisso) è necessaria un’operazione
di apertura (creazione dello stream). L’operazione di apertura
compie le azioni preliminari necessarie affinché il dispositivo possa
essere acceduto (in lettura o in scrittura).
• Accesso al flusso: una volta associato un flusso ad un dispositivo
fisico è possibile scambiare dati tra il dispositivo ed il programma.
• Chiusura del flusso: una volta terminate le operazioni di
lettura/scrittura sul dispositivo, è necessaria un’operazione di
chiusura per eliminare l’associazione tra il flusso ed il dispositivo.
File in C
Un File è un qualsiasi dispositivo in C, da un disco a un terminale
a una stampante, a cui sono associati i flussi.
Per associare un flusso a un file è necessario un’operazione di
apertura. Una volta aperto un file sarò possibile scambiare
informazioni tra il file e il programma.
Per eliminare l’associazione tra flusso e file è necessaria
un’operazione di chiusura.
Nel caso un file aperto in scrittura, l’eventuale contenuto del
flusso viene scritto sul dispositivo fisico.
File in C
Ogni flusso ha associato una struttura dati (struct) chiamata FILE
contenente i seguenti campi:
• Modalità di utilizzo del file (lettura, scrittura, o lettura e
scrittura);
• Posizione corrente su file (indicante il prossimo byte o
carattere da leggere o scrivere su file);
• Un indicatore di errore di lettura/scrittura;
• Un indicatore di end-of-file (EOF), indicante il raggiungimento
della fine del file.
Il tipo FILE è un tipo derivato definito in stdio.h.
Puntatore al File
I puntatori al file (file pointer) sono le variabili (puntatori)
fondamentali per la gestione del file.
Sono puntatori alle strutture dati che descrivono i file. Ogni
operazione di apertura a file restituisce un puntatore a una
variabile di tipo FILE, definito in stdio.h.
Il puntatore al file è una variabile puntatore al tipo FILE.
Per potere leggere e scrivere i file è necessario usare delle
variabili di tipo puntatore a file, dichiarate nel seguente modo:
FILE *fp;
Puntatore al File
Le strutture FILE non sono mai gestite direttamente dall’utente,
ma solo dalle funzioni della libreria standard stdio.h.
L’utente definisce e usa, nei suoi programmi, solo puntatori a
FILE.
Apertura di un File
Un file è aperto mediante la funzione fopen() avente il seguente prototipo:
FILE * fopen ( char * nomefile , char * modo)
• nomefile è una stringa di caratteri che rappresenta il nome del file (percorso)
• modo è una stringa che rappresenta la modalità di apertura del file
fopen dapprima apre il file e poi restituisce un puntatore di tipo FILE al file.
Se si verifica un errore in apertura del file, fopen restituisce un puntatore nullo
(NULL).
Esempio:
Apertura di un File
Le modalità "r+", "w+", "a+" (incluse quelle con l’aggiunta di "b" per i file binari) sono
dette di aggiornamento e permettono di leggere e scrivere lo stesso file
Apertura di un File – Sola Lettura
Chiamata per file di testo:
FILE * fp = fopen (<nome file>, "r"):
Chiamata per file binari:
FILE * fp = fopen (<nome file>, "rb")
Il file è acceduto solo per operazioni di lettura. La funzione restituisce NULL se il
file non esiste. In caso di successo, per la struttura FILE creata, il campo
indicatore di posizione assume il valore 0.
Apertura di un File – Sola Scrittura
Chiamata per file di testo:
FILE * fp = fopen (<nome file>, "w"):
Chiamata per file binari:
FILE * fp = fopen (<nome file>, "wb"):
Il file è acceduto solo per operazioni di scrittura. Se il file già esiste, il contenuto
corrente viene perso (sovrascritto).
Apertura di un File – Scrittura con Aggiunta
Chiamata per file di testo:
FILE * fp = fopen (<nome file>, "a"):
Chiamata per file binari:
FILE * fp = fopen (<nome file>, "ab"):
Il file è acceduto solo per operazioni di scrittura. Se il file già esiste, il contenuto
corrente non viene perso, e l’indicatore di posizione si porta inizialmente alla fine
del file.
Chiusura di un File
È fondamentale che i flussi aperti in un programma siano chiusi prima della
terminazione del programma (anche se il sistema operativo provvede alla chiusura
automaticamente, si liberano le risorse).
La chiusura scrive sul file eventuali dati rimasti nel buffer.
Errori nella chiusura del file possono portare alla perdita di dati, interi file o a errori di
esecuzione.
La chiusura libera l’area di controllo del file.
La chiusura di un file viene realizzata mediante la funzione fclose avente il seguente
prototipo:
int fclose (FILE *fp);
dove fp è il puntatore restituito dalla fopen.
fclose restituisce:
• 0 in caso di chiusura corretta;
• un valore diverso da 0 in caso di errore.
Lettura e scrittura formattata per file di testo
Abbiamo studiato l'utilizzo di:
scanf
●
printf
●
●Esistono le equivalenti funzioni per la
lettura/stampa su file di testo:
fscanf
●
fprintf
●
Lettura e scrittura formattata per file di testo
fprintf
int fprintf (FILE * fp, char * stringa_di_controllo,…..);
• Consente di scrivere testo formattato su un file di testo aperto in
modalità di scrittura o aggiornamento
• Generalizzazione di printf
• In caso di successo, la funzione restituisce il numero di caratteri
scritti nello stream e l’indicatore di posizione si porta in
corrispondenza del carattere successivo all’ultimo carattere scritto.
Altrimenti, la funzione restituisce EOF.
• EOF è una costante simbolica definita nel file di include stdio.h. La
costante simbolica EOF è definita in modo tale che non possa mai
eguagliare alcun carattere; solitamente vale −1.
Lettura e scrittura formattata per file di testo
FILE *fp;
int i;
...
i = 2;
fprintf(fp, "Il valore di i è %d\n", i);
Lettura e scrittura formattata per file di testo
fputc
int fputc (int c, FILE * fp);
• Consente di scrivere il carattere c su un file di testo
aperto in modalità di scrittura o aggiornamento
• La funzione converte c in un unsigned char e poi lo
scrive
• La funzione usa solo il byte di ordine più basso di c.
• In caso di successo, la funzione restituisce il carattere
scritto nel file e l’indicatore di posizione si porta in
corrispondenza del carattere successivo. Altrimenti, la
funzione restituisce EOF.
• Esempio: fputc('a',fp)
Lettura e scrittura formattata per file di testo
fputs
int fputs (char * s, FILE * fp);
• Consente di scrivere una riga (stringa di
caratteri) su un file di testo aperto in modalità
di scrittura o aggiornamento
• In caso di successo, la funzione restituisce un
intero non negativo l’indicatore di posizione si
porta in corrispondenza del carattere
successivo all’ultimo carattere scritto.
Altrimenti, la funzione restituisce EOF
Lettura e scrittura formattata per file di testo
char buf[100];
FILE *fp;
...
fputs(buf,fp);
buf è il vettore che contiene la riga da scrivere sul
file fp terminate con il carattere di fine stringa “\0”
Lettura e scrittura formattata per file di testo
Lettura e scrittura formattata per file di testo
fscanf
int fscanf (FILE * fp, char * stringa_di_controllo,…..);
• Consente di leggere testo in modo formattato da
un file di testo aperto in modalità di lettura o
aggiornamento.
• Generalizzazione di scanf
• In caso di successo, la funzione restituisce il
numero di caratteri letti dal file e l’indicatore di
posizione si porta in corrispondenza del carattere
successivo all’ultimo carattere letto. Altrimenti, la
funzione restituisce EOF
Lettura e scrittura formattata per file di testo
FILE *fp;
int i;
...
fscanf(fp,"%d", &i);
Lettura e scrittura formattata per file di testo
fgetc
int fgetc (FILE * fp);
• Consente di leggere il carattere correntemente puntato dall’indicatore di
posizione da uno stream di testo aperto in modalità di lettura o
aggiornamento.
• In caso di successo (non si è raggiunto il marcatore end-of-file di fine
dati), la funzione restituisce il carattere letto e l’indicatore di posizione si
porta in corrispondenza del carattere successivo. Altrimenti, la funzione
restituisce EOF.
• Esempio:
FILE *fp;
char c;
c = fgetc(fp);
Lettura e scrittura formattata per file di testo
fgets
char * fgets (char * s, int n, FILE * fp);
• Consente di leggere dalla posizione corrente di un file di testo
(aperto in modalità di lettura o aggiornamento) una stringa di al
più n-1 caratteri e memorizzarla nell’array di caratteri puntato da s
insieme al carattere ‘\0’ di terminazione stringa.
• Il numero effettivo k di caratteri letti può essere inferiore a n-1 se
il k-esimo carattere è un newline.
• In caso di successo, la funzione restituisce s. In caso di insuccesso
(errore o la funzione incontra il marcatore end-of-file), la funzione
restituisce NULL.
Lettura e scrittura formattata per file di testo
• Lettura riga per riga
char buf[100];
FILE *fp;
char *s;
int n;
...
n = 100;
s = fgets(buf, n, fp);
• legge fino a 100 caratteri o al carattere '\n’,
includendolo nella stringa
• aggiunge automaticamente a fine riga il carattere di fine
stringa “\0”.
• ritorna NULL in caso di errore oppure il valore di buf
Lettura righe in un file
#include <stdio.h>
int main(int argc, char **argv)
{ char buf[100]; int linee;
FILE *fp;
if( argc < 2 ) printf("Errato numero di parametri\n");
else {
fp = fopen(argv[1], "r");
if(fp!= NULL) {
linee = 0;
while(fgets(buf,100,fp) != NULL)
linee++;
fclose(fp);
printf("Il file contiene %d linee\n", linee);
}
else
printf("Il file %s non esiste\n",argv[1]);
}
}
Controllo sull’indicatore end-of-file
feof
int feof (FILE * fp);
• Consente di controllare se il file pointer è posizionato a fine
file (end-of-file)
• Restituisce 1 se il file pointer è posizionato a fine file e il
numero 0 in caso contrario.
int ret;
FILE *fp;
...
ret = feof(fp);
Conteggio caratteri numerici in file
Per sapere se il file pointer si trova posizionato a fine
file:
int ret;
FILE *fp;
...
ret = feof(fp);
●Il valore di ritorno ret conterrà il numero 1 se il file
pointer è posizionato a fine file e il numero 0 in caso
contrario.
Stream di default
Quando un programma va in esecuzione il sistema apre automaticamente tre file
pointer (stream di default o flussi standard):
• lo standard input – stdin (riceve input da tastiera): aperto in sola lettura.
• lo standard output – stdout (stampa output su schermo): aperto in sola scrittura.
• lo standard error – stderr (stampa messaggi di errore su schermo): aperto in sola
scrittura.
Lo standard input, lo standard output, e lo standard error vengono manipolati usando le
variabili globali puntatore di tipo FILE * stdin, stdout, e stderr definite in stdio.h.
Abbiamo le seguenti equivalenze:
● printf(…) → fprintf(stdout,...)
● scanf(…) → fscanf(stdin,...)
● getchar() → fgetc(stdin)
● putchar(c) → fputc(c, stdout)
● eof( ) → feof(stdin)
Stream di default
● gets legge una riga da tastiera ma elimina il carattere di newline
●puts scrive una riga a video aggiungendo automaticamente un
carattere di newline.
Ne consegue che :
● gets(buf) non equivale a fgets(buf, n, stdin)
● puts(buf) non equivale a fputs(buf, stdout)
Inoltre non abbiamo il controllo sulla dimensione del buffer
Printf
● %[flags][width][.precision][length]specifier
speci Output Example
fier
d or i Signed decimal integer 392
u Unsigned decimal integer 7235
o Unsigned octal 610
x Unsigned hexadecimal integer 7fa
X Unsigned hexadecimal integer (uppercase) 7FA
f Decimal floating point, lowercase 392.65
F Decimal floating point, uppercase 392.65
e Scientific notation (mantissa/exponent), 3.9265e+2
lowercase
E Scientific notation (mantissa/exponent), 3.9265E+2
uppercase
g Use the shortest representation: %e or %f 392.65
G Use the shortest representation: %E or %F 392.65
a Hexadecimal floating point, lowercase -0xc.90fep-2
A Hexadecimal floating point, uppercase -0XC.90FEP-2
c Character a
s String of characters sample
p Pointer address b8000000
flags description
- Giustificato sinistra. (destra è il default)
+ Forza la stampa del segno
(space Spazio bianco prima del valore se non c'è un
) segno
# Se usato con specificatori o, x o X il valore
è preceduto da 0, 0x o 0X rispettivamente per
valori diversi da 0.
Usato con a, A, e, E, f, F, g o G forza il punto
decimale anche senza cifre.
0 Aggiunge zero in testa quando viene usato
width. (vedi width sub-specifier).
Width
width description
(number Minimo numeri di caratteri da stampare. Se il
) valore non ne ha abbastanza vengono aggiunti
spazio, se di più non viene troncato.
* Il numero di caratteri non viene specificato
nella stringa di formato, ma passato come
parametro, subito prima del valore da stampare.
precision
.precisio description
n
.number Per (d, i, o, u, x, X): precision minimo numero di
cifre. Se il valore non ha cifre vengono stampati 0, se
più lungo non viene troncato.
Per a, A, e, E, f e F : numero di cifre stampate dopo il
punto decimale ( default: 6).
Per g e G : Massimo numero di cifre significative.
Per s: massimo numero di caratteri. (default stampa
fino a fine stringa).
.* precision non viene specificato nella stringa di
formato, ma passato come parametro prima del valore da
stampare.