informatica 2 – modulo c · gestione dei files in c •un file è una sequenza di bytes che...

41
Programmazione C Massimo Callisto De Donato [email protected] www.cs.unicam.it/massimo.callisto LEZIONE 9 FILE Università degli studi di Camerino Scuola di scienze e tecnologia - Sezione Informatica A.A. 2011/12

Upload: duongxuyen

Post on 29-Sep-2018

216 views

Category:

Documents


0 download

TRANSCRIPT

Programmazione C Massimo Callisto De Donato

[email protected]

www.cs.unicam.it/massimo.callisto

LEZIONE 9 – FILE

Università degli studi di Camerino Scuola di scienze e tecnologia - Sezione Informatica

A.A. 2011/12

Gestione dei files in C

• Un file è una sequenza di bytes che supportano le operazioni di: – creazione, cancellazione, posizionamento, lettura, scrittura.

• Un file può rappresentare qualsiasi cosa (file di testo, schermo, una stampante, un disco) chiamato canale. – Le operazioni saranno tradotte a seconda del canale su cui

operiamo.

• Esempio: – scrivere qualcosa su un file che rappresenta lo schermo produce

una stampa a video.

– La stessa operazione su una stampante provoca una stampa.

Gestione dei files in C

• Le classiche operazioni sono:

– Apertura • Possibilità di creazione

– Operazioni sul file • Lettura e scrittura.

• Posizionamento [solo su file allacciati a canali che lo supportano]

– Chiusura • Serve a scrivere eventuali dati rimasti in attesa da inviare al

canale.

Gestione dei files in C da stdio.h

Funzione Significato

fopen() Apre un file

fclose() Chiude un file

putc()/fputc() Scrive un carattere su file

getc()/fgetc() Legge un carattere da file

fgets()/fputs() Come sopra su stringhe

fread()/fwrite() Lettura/scrittura di byte da/su file

fprintf()/fscanf() Equivalente della printf/scanf su file

fseek() Posizionamento su un certo byte del file

feof() Raggiungimento fine file

ferror() Restituisce vero se si verifica un errore

rewind() Riporta l’indicatore di posizione all’inizio del file

fremove() Cancella il file

fflush() Scarica il contenuto del buffer in memoria

File: aggetti utili

• Per capire la posizione si usano:

– NULL: definisce il puntatore nullo

– EOF: identifica la fine del file

– Costanti di posizionamento(con fseek()) • SEEK_SET inizio file

• SEEK_CUR posizione corrente

• SEEK_END: fine del file

File: apertura

• Apertura: #include <stdio.h>

FILE *fopen(const char * filename, const char *mode);

• Il prototipo dice: – Una stringa filename in ingresso

– Una stringa mode che identifica come aprire il file

– Ritorna: • un puntatore a struttura di tipo FILE

• NULL se l’apertura non va a buon fine.

Esempio #include <stdio.h> // STanDard Input/Output

FILE *fp;

if((fp = fopen(“test.txt”, “r”)) == NULL){

printf(“Errore di apertura di „test.txt‟!\n”);

exit(-1);

}

// Possiamo operare sul file puntato da fp

File: la struttura FILE

• La struttura FILE è contenuta nella libreria stdio.h

• Mantiene le informazioni per la corretta gestione del file

[nome, modalità di apertura, posizione corrente, …]

• Le funzioni che operano sul file useranno queste informazioni a seconda dei casi.

• Es.: fwrite() controllerà nella struttura se abbiamo aperto il file in modalità corretta.

File: modalità di apertura

Modalità Descrizione

r Read File di testo in sola lettura [se non esiste torna NULL]

w Write File di testo in sola scrittura [ il contenuto viene cancellato,se il file

non esiste viene creato]

r+ Read & Write File di testo in lettura e scrittura [se non esiste torna NULL]

w+ Write & Read File di testo in scrittura e lettura [il contenuto viene cancellato, se il file non esiste viene creato]

a Append File di testo in scrittura aggiungendo i dati alla fine del file [ae il file non esiste viene creato].

a+ Append & Read Come append ma aggiunge anche la lettura.

b Binary Se una b viene aggiunta alle modalità precedenti si indica che il file è binario. [rb, wb, ab, e r+b, w+b, a+b]

File: modalità di apertura

• In modalità testo ciò che effettivamente contiene il file può non rispecchiare ciò che leggiamo/scriviamo.

• Esempio: new line può corrispondere – al carattere ‘\n’ oppure

– alla coppia di caratteri CarriageReturn + LineFeed (‘\r\n’).

• In modalità binario leggiamo ciò che si trova sul file [i due caratteri CR e LF]

Esempio #include <stdio.h>

#include <stdlib.h>

#define MAX_LEN 100

int main(int argc, char *argv[])

{

FILE *fp;

char mfile[MAX_LEN], mod[4];

while(1){

printf("File da aprire e modalita'[r|r+|w|w+|a|a+|...]:");

scanf("%s%s", mfile, mod);

if((fp = fopen(mfile, mod)) == NULL){

printf("Errore di apertura di %s!\n", mfile);

exit(-1);

}

printf("File %s aperto in modalità %s!\n", mfile, mod);

// do nothing

fclose(fp);

}

}

Esercizio

• Modificare il programma precedente per aprire un solo file nella modalità scelta.

• Il file su cui operare deve essere indicato all’avvio del programma.

• Schema di utilizzo:

programma.exe nome_file modalità

File: chiusura

• La chiusura del file è sempre un operazione importante:

– (in generale) l‘O.S. gestisce scritture bufferizzate.

• La chiusura forza a scrivere i dati ancora bufferizzati.

int fclose(FILE* fp);

– Restituisce zero se va a buon fine;

– EOF se c'è stato qualche errore;

• Quando può tornare EOF?

Es.: il file puntava ad un disco e prima della chiusura qualcuno lo ha estratto dal computer.

File: lettura/scrittura

• Dopo l’apertura di un file possiamo: – Leggere/scrivere caratteri: getc(), putc()

– Leggere/scrivere stringhe: fputs(), fgets()

– Leggere e scrivere con fscanf(), fprintf()

• In modalità binario leggiamo/salviamo tipi di dati arbitrari. Es.: – Possiamo creare una rubrica tramite le struct.

– Il salvataggio consiste nello scrivere le variabili di tipo struct su un file.

File: lettura di caratteri

• getc() [fgetc()] per la lettura da file di un carattere.

int getc(FILE* fp);

• Dopo la lettura fp punta al prossimo carattere

• La funzione ritorna: – Il carattere letto

– EOF per la fine del file

– EOF in caso di errore.

• Per distinguere tra errore o fine del file usiamo ferror() o feof()

Esempio #include <stdio.h>

#include <stdlib.h>

int main(int argc, char *argv[])

{

FILE *fp;

char ch;

if((fp = fopen("input.txt", "r")) == NULL){

printf("Errore di apertura di input.txt!\n");

exit(-1);

}

printf("File input.txt aperto il lettura!\n");

while( (ch = getc(fp)) != EOF ) {

printf( "letto %c [%d]\n", ch, ch);

//sleep(10); // se entro 20s elimino input.txt provoco un errore(?)

}

if(!feof(fp)){

printf("C'e' stato un errore!");

printf("%d", ferror(fp));

}

fclose(fp);

return 0;

}

File: scrittura di caratteri

• putc() [fputc()] per la scrittura su file di un carattere

int puntc(int ch, FILE* fp);

• Accoda alla posizione di fp il carattere ch.

• Ritorna: – Il ch carattere appena scritto

– EOF in caso di errore

Esempio #include <stdio.h>

#include <stdlib.h>

int main(int argc, char *argv[])

{

FILE *fp;

char ch, *ofile = "output.txt";

if((fp = fopen(ofile, “w")) == NULL){

printf("Errore di apertura di %s!\n“, ofile);

exit(-1);

}

printf("File %s aperto il scrittura!\n Termina con „0‟…\n“, ofile);

while( (ch = getchat()) != „0‟) {

if(putc(ch, fp) == EOF){printf(“Errore in scrittura!\n”); exit(-1);}

// Valeva anche: if(fputc(ch, fp) != ch){…}

}

fclose(fp);

return 0;

}

Esercizio

• Scrivere il programma copia_files.c che prende in input:

– il file da cui leggere

– il file su cui scrivere: copia_files.exe in.txt out.txt

• N.b: getc() e putc() non effettuano una copia esatta da in.txt a

out.txt, perché?

Esercizio

• Proviamo ad implementare il programma copia_files in modalità binaria.

• Osservazione 1:

dobbiamo aggiungere b alla modalità di apertura di un file.

• Osservazione 2:

in modalità binario un byte potrebbe rappresentare EOF e quindi potremmo leggere erroneamente la fine di un file. Per controllare la terminazione usiamo al suo posto feof(FILE *).

File: lettura di stringhe con fgets()

• Leggiamo il file a righe con fgets()

char *fgets(char *buffer, int n, FILE *fp);

• Inserisce in buffer i caratteri letti:

– Fino alla terminazione della riga (con ‘\n’ compreso); – Oppure più n-1 caratteri se la riga è più lunga di n. – A buffer si aggiunge automaticamente ‘\0’.

• Ritorna NULL in caso di errore.

FILE *fp = fopen(…);

char line[LINE_MAX]; // vedi LINE_MAX di limits.h

...

while (fgets(line, LINE_MAX, fp) != NULL){...}

File: scritture di stringhe con fputs()

• Scriviamo il contenuto di buffer nel file

int fputs(const char *buffer, FILE *fp);

• Non verranno aggiunti ‘\n’ in maniera automatica.

• Ritorna EOF se c’è stato errore, un intero positivo altrimenti.

FILE *fp = fopen(…);

char line[LINE_MAX];

scanf(“%s”, line);

if((fputs(line, fp) == EOF){ /* gestione errore */}

Esempio

int main(int argc, char *argv[])

{

FILE *fp;

char str[LINE_MAX];

int i = 0;

if(argc != 2){ printf("Errore! Usare: %s file_scrittura\n",

argv[0]); exit(-1);}

if((fp = fopen(argv[1], "w")) == NULL){ /*errore */ exit(-1); }

printf("Inserire il testo. Una riga vuota per terminare:\n");

while(1){

if(gets(str) == NULL) {… fclose(fp); exit(-1);}

if(!strlen(str)){ break; }

if(fputs(str, fp) == EOF) {… fclose(fp); exit(-1);}

if(fputs("\n", fp) == EOF) {…fclose(fp); exit(-1);}

}

fclose(fp);

} Il carattere ‘\n’ non era stato inserito in str.

Vedi gets() su Open Group

Esercizio

• Creiamo un programma conta_caratteri che:

– Usi le funzioni fgets() e fputs();

– Prende in input un file di testo input.txt;

– Per ogni riga calcola il numero dei caratteri presenti su una riga;

– Riscrive la riga su un altro file output.txt aggiungendo il numero dei caratteri trovato.

• Esempio:

input.txt output.txt

File: fprintf e fscanf • Date le funzioni printf e scanf, esistono le rispettive versioni che

operano su file in maniera esattamente uguale: int i = 1034;

FILE* fp = fopen(...);

fprintf(fp, "%d\n", i); // scrive su fp il valore 1034

fscanf(fp, "%d", &i); // legge da fp

• Otteniamo l’effetto delle funzioni note usando i puntatori a FILE definiti in stdio.h:

• standard input stdin (la tastiera) • standard output stdout (lo schermo) :

printf(...) fprintf(stdout, ...)

scanf(...) fscanf(stdin, ...)

getchar(...) fgetc(stdin)

putchar(c) fputc(c, stdin)

Esempio …

FILE *fp; char str[LINE_MAX]; int i = 0;

if(argc != 2){ fprintf(stdout, "Errore! Usare... "); exit(-1);}

if((fp = fopen(argv[1], "w")) == NULL){ /*errore */ exit(-1); }

printf("Inserire il testo. Una riga vuota per terminare:\n");

while(1){

if(fgets(str, LINE_MAX, stdin) == NULL) {… fclose(fp); exit(-1);}

if(strlen(str) < 2){ break; }

if(fputs(str, fp) == EOF || ) {… fclose(fp); exit(-1);}

//if(fputs("\n", fp) == EOF) {…fclose(fp); exit(-1);}

}

/* N.b.: fgets legge anche \n e lo memorizza in str; e se c‟è un solo

carattere deve essere \n */

File: operare in binario

• In modalità binario accediamo all’effettiva rappresentazione binaria del file.

#define BUFF_MAX 512

char buffer[BUFF_MAX];

int n;

FILE* fp = fopen(...);

n = fread(buffer, sizeof(char), BUFF_MAX, fp);

• Dati in ingresso: – Leggiamo sizeof(char) alla volta per BUFF_MAX volte; – Il contenuto va inserito in buffer

• La funzione scrive in n: – BUFF_MAX se c’erano abbastanza dati – < BUFF_MAX se il file è terminato prima

File: operare in binario

• In modalità binario accediamo all’effettiva rappresentazione binaria del file.

#define BUFF_MAX 512

char buffer[BUFF_MAX];

int n;

FILE* fp = fopen(...);

n = fwrite(buffer, sizeof(char), BUFF_MAX, fp);

• Dati in ingresso: – Scriviamo sizeof(char) alla volta per BUFF_MAX volte; – Il contenuto viene letto da buffer

• La funzione scrive in n il numero degli elementi scritti: – BUFF_MAX se è andato tutto bene – < BUFF_MAX se c’è stato un errore

Esempio /* Copia binaria tra due files. Gestione degli errori è omessa */

#define BUFF_MAX 512

FILE *fp_in, *fp_out;

char buffer[BUFF_MAX]; /* copiamo 1 byte (1 char)alla volta. */

int n = 0;

fp_in = fopen(“input.txt”, “rb”); fp_out = fopen(“output.txt”, “wb”);

while(!feof(fp_in)){

n = fread(buffer, sizeof(char), BUFF_MAX, fp_in);

if(fwrite(buffer, sizeof(char), n, fp_out) != n){

/* errore */ exit(-1);

}

}

fclose(fp_in); fclose(fp_out);

/* Nb:se al posto di char mettiamo int o altro potremmo avere dei troncamenti! */

Esercizio 1. Scriviamo un programma verify_1.c che:

– Prende in input due files;

– Verifica se i due files hanno lo stesso contenuto.

– Opera in modalità testo;

– Usi le funzioni fread, fwrite.

2. Scriverne un programma verify_2.c che opera come verify_1.c ma effettua il controllo in modalità binaria.

3. Scriverne verify.c che prende in input un ulteriore parametro:

– “text” per effettuare una verifica modalità testo;

– “bin” per effettuare una verifica in modalità binaria.

File: salvataggio tipi di dati derivati

• Scriviamo/leggiamo su un file strutture dati shop che contiene l’id di un negozio ed relativo nome.

#define MAX_LEN 100

#define MAX_ID 10

typedef struct {

char id[10];

char nome[MAX_LEN];

} shop;

• Operiamo in binario con append.

• Eseguendo più volte il programma la lista crescerà mostrando i negozi salvati in precedenza.

Esempio // nel main

shop buffer[N];

FILE *fp = fopen(“shop.save”, “ab”); // append binario

/* Assumiamo di riempire buffer[N] di N shops e salviamo il file*/

savefile(fp, buffer, N);

void save_file(FILE * file, const shop* buffer, const int size){

int n;

n = fwrite(buffer, sizeof(shop), size, file);

if(n != size){

printf("Errore di scrittura!\n");

exit(-1);

}

}

Esempio // nel main

FILE *fp = fopen(“shop.save”, “r”); // read binario

/* Riapriamo il file e richiamiamo una funzione che lo legge e ne

stampa il contenuto */

read_file (fp);

void read_file(FILE * file){

shop *ptr = (shop *) malloc(sizeof(shop));

if(ptr == NULL) { /* errore */ exit(-1);}

while(fread(ptr, sizeof(shop), 1, file)){

if(ferror(file)){ /* errore */ exit(-1); }

printf("[ID: %s, NOME: %s]\n", ptr->id, ptr->nome);

}

free(ptr); ptr = NULL;

}

File: spostamenti

• fseek() ci permette di muoverci a piacimento a partire da un punto di riferimento.

int fseek(FILE *fp, long offset, int pos);

– offset (anche negativo) è la quantità in bytes dello spostamento rispetto al riferimento pos – pos può essere

• inizio (SEEK_SET), • posizione corrente (SEEK_CUR), • fine file (SEEK_END)

• Ritorna: – 0 in caso di successo – -1 se c’è stato un errore

• E.s.: usiamo ftell() per conoscere la dimensione di un file in bytes.

Esempio /*

long ftell(FILE *fp); ritorna il n di bytes nella posizione

attuale rispetto all‟inizio del file

*/

shop buffer[N]; long n = 0;

FILE *fp = fopen(“shop.save”, “r”); // read dall‟inizio

n = fseek(fp, 0, SEEK_END);

if(!n) { /* errore */ exit(-1); }

printf(“Dimensione file: %l bytes”, ftell(fp));

/* ci riportiamo all‟inizio del file */

n = fseek(fp, 0, SEEK_SET);

if(!n) { /* errore */ exit(-1); }

File

• Passiamo all’esempio SaveStruct.c con i concetti visti prima.

• Il file è diventato un po’ lungo e comincia ad essere difficile da maneggiare

• Impariamo ad usare un file .h per lavorare meglio

Header files • Un file.h contiene solo:

– I prototipi delle funzioni

– Strutture dati derivate, alias, etc.

– Costanti

– Eventuali variabili globali

• Dovrà esistere il corrispettivo file.c che definisce i prototipi.

• Nel nostro file con il main includiamo il nostro file con:

#include “file.h”

Esempio // main.c

#include <stdio.h>

#include "bridge.h"

int main()

{

num();

return 0;

}

// file.c

#include <stdio.h>

/*static*/ int ggl = 0;

int num(){

printf("Ciao mondo");

ggl++;

return 0;

}

// bridge.h

int num();

1

2

3

Esempio #include <stdio.h>

#include "my_math.h"

int main(){

int a, b, c;

fprintf(stdout, “Tre numeri prego:");

fscanf(stdin, "%d %d %d", &a, &b, &c);

printf(“La media e‟ %f\n", average(a,b,c));

return 0;

}

/* my_math.h */

#define ZERO 0

float average(int,int,int);

float sum(int, int);

/* my_math.c */

#include "my_math.h”

float average(int x, int y, int z){

int somma;

return (somma =

sum(sum(x, y), z)) ==

ZERO ? ZERO : (somma / 3);

}

float sum(int x, int y){

return x + y;

}

Esercizio (ultimo )

• Creare un programma Rubrica che mantenga le informazioni sui contatti (e.s.: nome, cognome).

• Ciascun contatto possiede un ID unico nella rubrica.

• Il programma deve offrire un menu interattivo per:

– Apertura da file di una rubrica

– Salvataggio su file della rubrica attualmente in memoria

– Stampa lista contatti in memoria

– Aggiunta di un nuovo contatto

– Cancellazione di un contatto dalla rubrica

– Ricerca contatto

– Analisi del contenuto di un file di una rubrica

– Duplicazione del file di una rubrica

The end

Grazie per l’attenzione