GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma...

38
GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi-stenza dei dati) è necessario poterli archi-viare su memoria di massa. dischi nastri cd ...

Transcript of GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma...

Page 1: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

GESTIONE DEI FILE

Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi-stenza dei dati) è necessario poterli archi-viare su memoria di massa. dischi nastri cd ...

Page 2: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

IL CONCETTO DI FILE

• Un file è una astrazione fornita dal sistema operativo, il cui scopo è consentire la memo-rizzazione di informazioni su memoria di massa.

• Concettualmente, un file è una sequenza di registrazioni (record) uniformi, cioè dello stesso tipo.

• Un file è un’astrazione di memorizzazione di dimensione potenzialmente illimitata (ma non infinita), ad accesso sequenziale.

Page 3: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

IL CONCETTO DI FILE

• Una testina di lettura/scrittura (concettuale) indica in ogni istante il record corrente:

– inizialmente, la testina si trova per ipotesi sulla prima posizione

– dopo ogni operazione di lettura / scrittura, essa si sposta sulla registrazione successiva.

• È illecito operare oltre la fine del file.

Page 4: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

OPERARE SUI FILE

• A livello di sistema operativo un file è denotato univocamente dal suo nome assoluto, che comprende il percorso e il nome relativo.

• In certi sistemi operativi il percorso può comprendere anche il nome dell’unità.

• in DOS o Windows:

C:\temp\prova1.c

• in UNIX e Linux:

/usr/temp/prova1.c

Page 5: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

APERTURA DI UN FILE

• Poiché un file è un’entità del sistema opera-tivo, per agire su esso dall’interno di un programma occorre stabilire una corri-spondenza fra:

• il nome del file come risulta al sistema operativo

• un nome di variabile definita nel programma.

• Questa operazione si chiama apertura del file ed è concettualmente un’operazione del modello di coordinazione.

Page 6: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

APERTURA E CHIUSURA DI UN FILE

• Una volta aperto il file, il programma può operare su esso operando formalmente sulla variabile definita al suo interno: il sistema operativo provvederà a effettuare realmente l’operazione richiesta sul file associato a tale simbolo.

• Al termine, la corrispondenza fra nome del file e variabile usata dal programma per operare su esso dovrà essere distrutta, mediante l’operazione di chiusura del file.

Page 7: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

FILE IN C

• Per gestire i file, il modello di coordinazione del C definisce il tipo FILE.

• FILE è una struttura definita nello header standard stdio.h, che l’utente non ha necessità di conoscere nei dettagli – e che spesso cambia da un compilatore all’altro!

• Le strutture FILE non sono mai gestite direttamente dall’utente, ma solo dalle funzioni della libreria standard stdio.

• L’utente definisce e usa, nei suoi program-mi, solo dei puntatori a FILE.

Page 8: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

IL MODELLO DI FILE DEL C

Libreria standard stdio

l’input avviene dal canale di input associato a un file aperto in lettura

l’output avviene sul canale di output associato a un file aperto in scrittura

Due tipi di file: file binari e file di testo basterebbero i file binari, ma fare tutto con essi

sarebbe scomodo i file di testo, pur non indispensabili, rispondono a

un’esigenza pratica molto sentita.

Page 9: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

FILE IN C: APERTURA

• Per aprire un file si usa la funzione:

FILE* fopen(char fname[], char modo[])

Questa funzione apre il file di nome fname nel modo specificato, e restituisce un puntatore a FILE (che punta a una nuova struttura FILE appositamente creata).

• ATTENZIONE alle convenzioni dipendenti dal sistema operativo usato (\ nei percorsi oppure /, presenza o assenza di unità, etc)

Page 10: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

FILE IN C: APERTURA

Per aprire un file si usa la funzione:

FILE* fopen(char fname[], char modo[])

modo specifica come aprire il file: r apertura in lettura (read) w apertura in scrittura (write) a apertura in aggiunta (append)

• seguita opzionalmente da: t apertura in modalità testo (default) b apertura in modalità binaria

• ed eventualmente da + apertura con possibilità di modifica.

Page 11: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

FILE IN C: APERTURA

Per aprire un file si usa la funzione:

FILE* fopen(char fname[], char modo[])

• Il valore restituito da fopen() è un punta-tore a FILE, da usare in tutte le succes-sive operazioni sul file.

– esso è NULL in caso l’apertura sia fallita– controllarlo è il solo modo per sapere se il file

si sia davvero aperto: non dimenticarlo!

• I tre canali predefiniti standard (stdin, stdout, stderr) sono in tutto e per tutto dei file già aperti: quindi, il loro tipo è FILE*.

Page 12: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

FILE IN C: CHIUSURA

Per chiudere un file si usa la funzione:

int fclose(FILE*)

• Il valore restituito da fclose() è un intero– 0 se tutto è andato bene– EOF in caso di errore.

• Prima della chiusura, tutti i buffer vengono svuotati.

Page 13: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

FILE BINARI

Un file binario è una sequenza di byte: come tale, può essere usato per archivia-re su memoria di massa qualunque tipo di informazione

input e output avvengono sotto forma di una sequenza di byte

la lunghezza del file è registrata dal sistema operativo

la fine del file è rilevata basandosi sull’esito delle operazioni di lettura

Page 14: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

FILE BINARI

• Poiché un file binario è una sequenza di byte, sono fornite due funzioni per leggere e scrivere sequenze di byte

•fread() legge una sequenza di byte

•fwrite() scrive una sequenza di byte

• Essendo pure sequenze di byte, esse non sono interpretate: l’interpretazione è “negli occhi di chi guarda”.

• Quindi, possono rappresentare qualunque informazione (testi, numeri, immagini...)

Page 15: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

OUTPUT BINARIO: fwrite()

Sintassi:

int fwrite(addr, int dim, int n, FILE *f);

• scrive sul file n elementi, ognuno grande dim byte (complessivamente, scrive quindi ndim byte)

• gli elementi da scrivere vengono prelevati in memoria a partire dall’indirizzo addr

• restituisce il numero di elementi (non di byte!) effettivamente scritti (possono essere meno di n): dunque, quando restituisce zero significa che il file è finito.

Page 16: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

INPUT BINARIO: fread()

Sintassi:

int fread(addr, int dim, int n, FILE *f);

• legge dal file n elementi, ognuno grande dim byte (complessivamente, legge quindi ndim byte)

• gli elementi da leggere vengono scritti in memoria a partire dall’indirizzo addr

• restituisce il numero di elementi (non di byte!) effettivamente letti (possono essere meno di n, al limite anche zero nel caso di fine file). Dunque, quando restituisce zero significa che il file è finito.

Page 17: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

ESEMPIO 1

Salvare su un file binario numeri.dat il contenuto di un array di dieci interi.

#include <stdio.h>#include <stdlib.h>

main(){ FILE *fp; int vet[10] = {1,2,3,4,5,6,7,8,9,10};

if ((fp = fopen("numeri.dat","wb"))==NULL) exit(1); /* Errore di apertura */

fwrite(vet, sizeof(int), 10, fp); fclose(fp);} L’operatore sizeof è essenziale

per la portabilità

La funzione exit() fa terminare il programma anticipatamente.

Page 18: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

ESEMPIO 2

Leggere da un file binario numeri.dat una sequenza di interi, scrivendoli in un array.

#include <stdio.h>#include <stdlib.h>

main(){ FILE *fp; int vet[40], i, n;

if ((fp = fopen("numeri.dat","rb"))==NULL) exit(1); /* Errore di apertura */

n = fread(vet,sizeof(int),40,fp);

for (i=0; i<n; i++) printf("%d ",vet[i]);

fclose(fp);}

fread tenta di leggere 40 interi, ma ne legge meno se il file finisce prima (come qui)

n contiene il numero di interi effettivamente letti

Page 19: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

ESEMPIO 3

Scrivere su un file di caratteri testo.txt una sequenza di caratteri.

#include <stdio.h>#include <stdlib.h>

main(){ FILE *fp; int n; char msg[] = "Ah, l'esame\nsi avvicina!";

if ((fp = fopen("testo.txt","wb"))==NULL) exit(1); /* Errore di apertura */

fwrite(msg, strlen(msg), 1, fp); fclose(fp);} Un carattere ha sempre size=1

Scelta: non salvare il terminatore.

Dopo averlo creato, provare ad aprire questo file con un editor qualunque (es. blocco note)

Page 20: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

ESEMPIO 4

Leggere da un file di caratteri testo.txt una sequenza di interi, salvandoli in una stringa.

#include <stdio.h>#include <stdlib.h>

main(){ FILE *fp; int msg[80], n;

if ((fp = fopen("testo.txt","rb"))==NULL) exit(1); /* Errore di apertura */

n = fread(msg,1,80,fp);

puts(msg);

fclose(fp);} n contiene il numero di interi effetti-

vamente letti.. ma non ci interessa!

Idea: perché non provare ad leggere un file (corto) creato con un editor qualunque?

Page 21: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

OUTPUT DI NUMERI

L’uso di file binari consente di rendere evidente la differenza fra la rappresentazione interna di un numero e la sua rappresentazione esterna come stringa di caratteri in una certa base.

Supponiamo che sia int x = 31466; Che differenza c’è fra

printf("%d", x);

e

fwrite(&x, sizeof(int), 1,stdout); ?

Page 22: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

OUTPUT DI NUMERI

Se x è un intero che vale 31466, internamente la sua rappresentazione in complemento a due è (ipotesi: interi lunghi 16 bit):

01111010 11101010 Perciò,

emettendo direttamente tale sequenza di byte, come farebbe fwrite()

e interpretandoli come se fossero caratteri, come accade se li si invia sul video (stdout)

si otterranno i caratteri corrispondenti al codice

ASCII di quei byte: êz

Page 23: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

OUTPUT DI NUMERI

Per ottenere invece la stringa “31466”, che rappresenta il numero in base dieci, occorre convertire il numero in stringa

in TurboC, con la funzione itoa() [non standard] oppure con la nostra numToS()

e poi stampare la stringa così ottenuta: puts()

il che è esattamente quello che fa printf() !!

Page 24: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

INPUT DI NUMERI

Analogamente, che differenza c’è fra

scanf("%d", &x); e fread(&x, sizeof(int), 1,stdin);

nell’ipotesi di battere da tastiera “23” ? Anche qui,

scanf() preleva una stringa di caratteri adeguati alla sintassi di un intero decimale e li converte in numero, ottenendo ventitre

viceversa, fread() prende due caratteri (la size di un int) e scrive dentro a x i loro codici ASCII, inter-pretandoli poi come intero. Risultato (assurdo): tredicimilacentosei !

Page 25: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

FILE DI TESTO

È un caso particolare di file binario, che coinvolge una sequenza di caratteri

Ha senso trattarlo come caso a parte per-ché i caratteri sono un caso estremamen-te frequente, con caratteristiche proprie: esiste un concetto di riga e di fine riga (‘\n’) certi caratteri sono stampabili a video

(quelli di codice 32), altri no la sequenza di caratteri è chiusa dal carattere

speciale EOF

Page 26: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

FILE DI TESTO (segue)

La lunghezza del file è sempre registrata dal sistema operativo (come per ogni file binario)…

... ma è anche indicata in modo esplicito dalla presenza del carattere EOF.

Quindi, la fine del file può essere rilevata o in base sull’esito delle operazioni di lettura o perché si intercetta il carattere di EOF.

Attenzione: lo speciale carattere EOF (End-Of-File) varia da una piattaforma all’altra.

Page 27: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

FILE DI TESTO & CANALI STANDARD

I canali di I/O standard non sono altro che file di testo già aperti stdin è un file di testo aperto in lettura, di

norma agganciato alla tastiera stdout è un file di testo aperto in scrittura, di

norma agganciato al video stderr è un altro file di testo aperto in scrittu-

ra, di norma agganciato al video

Le funzioni di I/O disponibili per i file di testo sono una generalizzazione di quelle già note per i canali di I/O standard.

Page 28: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

CONFRONTO

Funzione da console Funzione da file

int getchar(void); int fgetc(FILE* f);

int putchar(int c); int fputc(int c, FILE* f);

char* gets(char* s); char* fgets(char* s, int n, FILE* f);

int puts(char* s); int fputs(char* s, FILE* f);

int printf( ... ); int fprintf(FILE* f, ... );

int scanf( ... ); int fscanf(FILE* f, ... );

tutte le funzioni da file acquistano una “f” davanti nel nome (qualcuna però cambia leggermente nome)

tutte le funzioni da file hanno un parametro in più, che è appunto il puntatore al FILE aperto

sempre davanti… tranne in fgets/fputs

Page 29: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

PECULIARITÀ: fgets() vs. gets()

fgets() prevede anche un parametro intero n, che consente di leggere non più di n-1 di caratteri

char* fgets(char s[], int n, FILE* f)

È una caratteristica importante per non superare la lunghezza massima della stringa s.

A differenza di gets(), che lo elimina, fgets() mantiene il carattere di fine riga, se presente nella stringa letta;aggiunge comunque in fondo il terminatore ‘\0’.

Page 30: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

PECULIARITÀ: fputs() vs. puts()

A differenza di puts(), che lo aggiunge sempre, fputs() non inserisce in fondo il carattere di fine riga

Però, nessuna trascrive il terminatore ‘\0’

Page 31: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

PECULIARITÀ: getchar() vs. fgetc() putchar() vs. fputc()

getchar() e putchar() sono delle scorciatoie linguistiche per fgetc() e fputc()

getchar() fgetc(stdin)putchar(c) fputc(stdout, c)

in effetti, getchar() e putchar() sono quasi sempre delle macro!

Page 32: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

FUNZIONI SUI FILE: PECULIARITÀ

Esistono poi alcune funzioni per i file di testo che non hanno un analogo sui canali standard:

feof() indica se si è già incontrato EOF

perror() stampa un messaggio di errore sulcanale standard di errore (stderr)

fseek() sposta la testina di lettura/scritturasu una posizione a scelta nel file

ftell() dà la posizione corrente dellatestina di lettura/scrittura nel file

Page 33: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

ESEMPIO 1

Salvare su un file di testo prova.txt ciò che viene battuto sulla tastiera.

#include <stdio.h>#include <stdlib.h>

main(){ FILE *fp; if ((fp = fopen("prova.txt","w"))==NULL) exit(1); /* Errore di apertura */ else { int c;

while ((c=getchar())!=EOF) fputc(c,fp); fclose(fp); }} Per generare EOF, CTRL+Z (DOS /

Windows) o CTRL+D (Unix/Linux)

fp può essere NULL se non c’è spazio su disco o se il disco è protetto da scrittura.

Page 34: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

ESEMPIO 2

Stampare a video il contenuto di un file di testo prova.txt.

#include <stdio.h>#include <stdlib.h>

main(){ FILE *fp; if ((fp = fopen("prova.txt","r"))==NULL) exit(1); /* Errore di apertura */ else { int c;

while ((c=fgetc(fp))!=EOF) putchar(c); fclose(fp); }}

fp può essere NULL se il file richiesto non esiste.

Page 35: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

ESEMPIO 3

Scrivere un programma che, dato un file di testo prova.txt, sostituisca tutte le minuscole in maiuscole.

Occorre poter leggere e poi riscrivere un carattere apertura con modifica

"r+" presuppone che il file esista, lo apre in lettura ma consente anche (alternatamente) di scriverci sopra

"w+", pur consentendo delle letture, crea il file se non esiste o lo cancella se già esiste

"a+" è analoga a "r+", ma agisce in coda al file.

Nel caso in esame serve la modalità r+.

Page 36: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

ESEMPIO 3 (segue)

È inoltre necessario potersi collocare in una ben precisa posizione sul file, per poter sostituire una minuscola nella corrispondente maiuscola.

In particolare, quando si legge una minuscola:

• si retrocede di una posizione

• la si sovrascrive con la maiuscola.

•A questo provvedono fseek() e ftell().

ATTENZIONE: la modalità r+ consente di alternare letture e scritture, ma con l’obbligo di effettuare una fseek() per passare da lettura a scrittura o viceversa.

Page 37: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

fseek() e ftell(): SINTASSI

Sintassi

int fseek(FILE* f, long offs, int orig)long ftell(FILE* f)

dove:

offs dà la posizione, rispetto a orig, a cui portarsi sul file

orig dà la posizione rispetto a cui misurare offs, e può essere:

– l'inizio del file SEEK_SET

– la posizione corrente nel file SEEK_CUR

– la fine del file SEEK_END

NB: per un file di testo, offs deve valere o 0 o un valore resti- tuito da ftell(), nel qual caso, orig deve essere SEEK_SET

Page 38: GESTIONE DEI FILE Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persi- stenza dei dati) è necessario poterli archi-

ESEMPIO 3

int main() { FILE *file; char fname[20]; int ch; printf("Nome del file: "); scanf("%s", fname); if ((file=fopen(fname, "r+"))==NULL) { perror("Impossibile aprire file di input\n");

exit(1); } while((ch=fgetc(file))!=EOF)

if(islower(ch)) { fseek(file, ftell(file)-1, SEEK_SET); fputc(toupper(ch), file); fseek(file, 0, SEEK_CUR); /* OBBLIGO! */

} fclose(file); exit(0);}

non fa nulla, ma è obbligatoria per alternare letture e scritture.