la libreria standard del c - Dipartimento di Ingegneria Informatica e

Transcript

la libreria standard del c - Dipartimento di Ingegneria Informatica e
LA LIBRERIA
STANDARD DEL C
La libreria standard del C
z
z
z
La libreria standard del C è in realtà un insieme di
librerie
Per usare una libreria, non occorre inserirla
esplicitamente. Ogni file sorgente che ne faccia uso
deve includere lo header opportune che contiene le
dichiarazioni necessari
Librerie standard
z
z
z
z
z
input-output
funzioni matematiche
gestione di stringhe
operazioni su caratteri
Fondamenti di Informatica
gestione dinamica della memoria
stdio.h
math.h
string.h
ctype.h
stdlib.h
2
1
Input/Output
Lettura e scrittura Caratteri e
Stringhe: Terminale e file
Input/output
console
caratteri
stringhe
file
formattato
ascii
Fondamenti di Informatica
binari
3
Il modello di coordinazione del C
Libreria standard stdio.h
z
z
z
z
z
z
z
l’input avviene di norma dal canale standard di input (stdin)
l’output avviene di norma dal canale standard di output
(stdout)
input e output avvengono sotto forma di una sequenza di
caratteri
tale sequenza è terminata dal carattere speciale EOF (end
of file), che varia da una piattaforma ad un’altra
di norma il canale standard stdin coincide con la tastiera
di norma il canale standard stdout coincide con il video
esiste inoltre un altro canale di output, riservato ai messaggi
di errore: stderr
Fondamenti di Informatica
4
2
Il modello di coordinazione di base
Poiché sui canali di I/O fluiscono insiemi
di caratteri il modello di coordinazione
prevede due operazioni base
z
z
scrivere un carattere su un canale di output
ƒ
z
putchar(ch);
leggere un carattere dal canale si input
ƒ
ch = getchar();
Ogni altro tipo di I/O può essere costruito a
partire da queste operazioni primitive
z
Fondamenti di Informatica
5
Lettura e Scrittura di Caratteri
int getchar(int c);
z
z
z
z
z
z
stdio.h
legge un carattere sul canale di intput stdin
restituisce il carattere letto, EOF nel caso che la sequenza sia finita
e in caso di errore
la stdin è generalmente la tastiera ma potrebbe non esserlo
la lettura è bafferizzata
effettua l’echo e attende invio
int putchar(int c);
z
z
z
stdio.h
scrive un carattere sul canale di output (di norma il monitor)
restituisce il carattere scritto, EOF in caso di errore
Fondamenti di Informatica
6
3
Lettura e Scrittura di Caratteri
Non sono standard e quindi non vanno usate
int getch(void);
void printstring(char s[])
{while (*s)
putschar(*s++);
putchar(‘\n’);
}
conio.h
z ritorna immediatamente
ignorando il buffer
z non attende l’invio
void readstring(char s[])
z non è standard
{int c;
z non fa l’eco sullo schermo
while ((c=getchat()) != EOF && c !=‘\n’)
*s++=c;
*s
=
‘\0’;
int getche(void);
}
z come getch() ma fa l’eco
sullo schermo.
z
Fondamenti di Informatica
7
Lettura e Scrittura di Stringhe
z
char * gets( char *s );
z
z
legge una stringa da tastiera e la
#include <stdio.h>
memorizza nella locazione indicata
#include <string.h>
nel parametro s. L’ingresso dei dati
viene concluso con un CR che viene void main( void ) {
trasformato in \0
char
s[80];
char * puts(char *s);
z
visualizza sullo schermo il contenuto
dell’argomento di tipo stringa, seguito
da un CR. Questa funzione riconosce
}
i caratteri speciali. Il tempo
necessario alla sua esecuzione e’
inferiore a quello necessario per
printf()
Fondamenti di Informatica
do {
gets(s); puts(s);
} while( strlen(s) != 0 );
8
4
I/O di valori numerici
Dato un numero intero calcolare la stringa di caratteri
che lo rappresenta e scrivere tale stringa sull’output
standard
z
void printInteger(int x) {
char s[20];
itoa(x,s,2);
puts(s);
}
Dato una stringa calcolare il numero intero che
rappresenta
z
void readInteger(int * x) {
char s[20];
gets(s);
*x = atoi(s);
}
Fondamenti di Informatica
9
Input/Output Formattato
z
z
Le operazioni di ingresso/uscita avvengono sempre tramite
un formato che resta sotto il controllo del programmatore.
int printf( “stringa di controllo”, elencoArgomenti );
la stringa di controllo puo’ contenere due tipi di elementi:
z
caratteri che vengono scritti inalterati sullo schermo.
direttive di conversione che descrivono il modo in cui gli argomenti
devono essere visualizzati. Le direttive di conversione sono costituite
da un segno % seguito dal carattere di conversione. printf() consente
l’utilizzo di un numero molto grande di specificatori di formato.
La funzione restituisce il numero di caratteri scritti o un
numero negativo in caso di errore
printf(“ora scrivo un intero %d, oppure una %s\n”, 10, “Stringa”);
Fondamenti di Informatica
10
5
Input/Output Formattato
%c
%d, %i
%e, %E
%f
%g, %G
%o
%s
%u
%x, %X
%p
%%
singolo carattere
intero con segno
notazione scientifica
virgola mobile
il piu’ breve fra %e e %f
ottale senza segno
stringa di caratteri
intero senza segno
esadecimale
puntatore
Fondamenti di Informatica
visualizza %
a
-10
1e+10
1.25
xF5
11
Modificatori di Formato
z
Gli specificatori di formato possono avere varianti che
cambiano parzialmente il significato, specificando
l’ampiezza dei campi, il numero delle cifre decimali e
l’allineamento rispetto al margine sinistro.
Ampiezza minima del campo: un intero posto fra il segno % e lo
specificatre di formato %10d
Precisione: permette di specificare il numero di cifre decimali in
un numero in virgola mobile. %10.3f
Allineamento: un segno - dopo il segno % fa in modo che il
campo venga stampato allineato a sinistra. %-10s
Il modificatore # fa stampare la variabile di tipo g, f ed e sempre
con il punto decimale. Nel caso del formato x premette il prefisso
0x
Fondamenti di Informatica
12
6
z
z
z
z
int scanf( char * stringaDiControllo, elencoArgomenti)
permette di leggere dati dal dispositivo di ingresso
Gli specificatori di formato sono analoghi a quelli della
funzione printf. La presenza di caratteri diversi da quelli di
formato permettono la lettura di questi caratteri senza che
vengano memorizzati.
Gli argomenti di scanf sono indirizzi
E’ possibile specificare alcuni modificatori di formato fra cui
la massima ampiezza del campo da leggere. Ad esempio
scanf(“%10s %10s”, ......... ) legge al massimo dieci caratteri
per ogni stringa
Fondamenti di Informatica
13
I Files
7
I file
z
Per poter mantenere disponibili i dati tra le diverse esecuzioni
di un programma (persistenza dei dati) è necessario poterli
archiviare su memoria di massa
z
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
(record) dello stesso tipo
Un file è un’astrazione di memorizzazione di dimensione
potenzialmente illimitata (ma non infinita), ad accesso
sequenziale
z
z
Fondamenti di Informatica
15
Il concetto di file
z
Una testina di lettura/scrittura (concettuale)
indica in ogni istante il record corrente:
z
z
z
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
Fondamenti di Informatica
16
8
File e Sistema Operativo
z
z
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à
z
z
in DOS o Windows:
z C:\temp\prova1.c
In UNIX e Linux:
z /usr/temp/prova1.c
Fondamenti di Informatica
17
Apertura e chiusura di un file
z
Poiché un file è un’entità del sistema operativo, per agire su
esso dall’interno di un programma occorre stabilire una
corrispondenza fra:
z
z
z
z
il nome del file come risulta al sistema operativo
un nome di variabile definita nel programma
Dopo l'apertura, il programma opera sul file utilizzando la
variabile che lo rappresenta: il sistema operativo provvederà a
effettuare l’operazione richiesta sul file associato a tale
simbolo
Al termine, la corrispondenza dovrà essere soppressa:
operazione di chiusura del file
Fondamenti di Informatica
18
9
File in C
z
Il tipo FILE è una struttura definita in header
standard stdio.h, che l’utente non ha necessità di
conoscere nei dettagli (che spesso cambia da un
compilatore all’altro)
z
Le strutture FILE non sono mai gestite direttamente
dall’utente, ma solo dalle funzioni della libreria
standard stdio
z
L’utente definisce e usa, nei suoi programmi, solo
dei puntatori a FILE
Fondamenti di Informatica
19
Il modello di file in C
z
z
z
z
Libreria standard stdio
l’input avviene da un canale di input associato a un
file aperto in lettura
l’output avviene su un canale di output associato a
un file aperto in scrittura
Due tipi di file: file binari e file di testo
z
z
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
Fondamenti di Informatica
20
10
File in C: apertura
Per aprire un file si usa la funzione:
FILE* fopen(char fname[], char modo[])
z
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)
z
ATTENZIONE alle convenzioni dipendenti dal
sistema operativo usato (\ oppure / nei
percorsi, presenza o assenza di unità, …)
Fondamenti di Informatica
21
File in C: apertura
FILE* fopen(char fname[], char modo[])
z
modo specifica come aprire il file:
z
z
z
z
seguita opzionalmente da:
z
z
z
r apertura in lettura (read)
w
apertura in scrittura (write)
a
apertura in aggiunta (append)
t apertura in modalità testo (default)
b
apertura in modalità binaria
ed eventualmente da
z
+
di Informatica
apertura conFondamenti
possibilità
di modifica
22
11
File in C: apertura
FILE* fopen(char fname[], char modo[])
z
Il valore restituito da fopen() è un puntatore a FILE,
da usare in tutte le successive operazioni sul file:
z
z
z
z
NULL in caso l’apertura sia fallita
controllarlo è il solo modo per sapere se il file si sia
davvero aperto
se non si è aperto, il programma non può proseguire
Æ procedura exit()
I canali predefiniti standard (stdin, stdout, stderr)
sono dei file già aperti: quindi, il loro tipo è FILE*
Fondamenti di Informatica
23
File in C: chiusura
Per chiudere un file si usa la funzione:
int fclose(FILE*)
z
Il valore restituito da fclose() è un intero:
z
z
z
0 se tutto è andato bene
EOF in caso di errore
Prima della chiusura, tutti i buffer vengono
svuotati
Fondamenti di Informatica
24
12
Fine del file
z
La fine del file può essere rilevata:
z
o in base all’esito delle operazioni di lettura
È l'approccio standard del C: prima si tenta una operazione
di lettura, poi si guarda se è andata a buon fine,
controllando il valore da essa restituito
z
o perché si intercetta il carattere di EOF
Attenzione: lo speciale carattere EOF (End-Of-File) varia
da una piattaforma all’altra
Fondamenti di Informatica
25
File di testo
z
Un file di testo è un file che contiene
sequenze di caratteri
z
È un caso estremamente frequente, con
caratteristiche proprie:
z
z
z
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
Fondamenti di Informatica
26
13
File di testo e canali standard
z
I canali di I/O standard non sono altro che file di
testo già aperti
z
z
z
z
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 scrittura, 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
Fondamenti di Informatica
27
File di testo e canali standard
Funzione da console
Funzione da file
int getchar(void);
int putchar(int c);
char* gets(char* s);
int puts(char* s);
int printf(...);
int scanf(...);
int fgetc(FILE* f);
int fputc(int c, FILE* f);
char* fgets(char* s,int n, FILE*
f);
fputs(char* s,f,
FILE*
f);
int fprintf(FILE*
...);
z
z
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
Fondamenti di Informatica
28
14
I/O di caratteri: getchar() vs. fgetc()
z
int fgetc(FILE* f);
z
z
int fputc(int c, FILE* f);
z
z
Restituisce il successivo carattere contenuto nel file f,
oppure EOF se incontra la fine del file
Scrive il carattere c sul file f. Restituisce il carattere scritto,
oppure EOF in caso di errore
getchar() e putchar() sono delle scorciatoie
linguistiche per fgetc() e fputc()
getchar()
putchar(c)
≡
≡
fgetc(stdin)
fputc(c, stdout)
Fondamenti di Informatica
29
I/O di linee: fputs e fgets
z
char* fgets(char* s, int n, FILE* f);
z
z
z
z
Legge al più i successivi n-1 caratteri dal file f e li inserisce nel
vettore s, bloccandosi prima se incontra un carattere di fine linea
(’\n’)
La stringa s viene terminata con uno ‘\0’
Restituisce s, oppure NULL se incontra la fine del file oppure
rileva un errore
int fputs(char* s, FILE* f);
z
z
Scrive la stringa s sul file f.
Restituisce un valore non negativo se non ci sono errori, oppure
EOF se si verifica un errore
Fondamenti di Informatica
30
15
I/O formattato: fprintf e fscanf
int fprintf(FILE* f, ...);
int fscanf(FILE* f, ...);
z
fprintf e fscanf operano così come le rispettive
funzioni (printf e scanf) su stdin e stdout, ma
agiscono sul file f indicato come primo parametro
Fondamenti di Informatica
31
Esempio 1
z
Salvare su un file di testo prova.txt ciò che viene
battuto sulla tastiera
fp è NULL se non c’è spazio su
#include <stdio.h>
disco o esso è protetto da scrittura
#include <stdlib.h>
main(){
FILE* fp;
fp = fopen("prova.txt","w");
if (fp==NULL) exit(1); /* Non si è aperto */
else {
int c;
while ((c=getchar())!=EOF)
Per generare EOF:
fputc(c, fp);
- CTRL+Z (su DOS/Windows)
- CTRL+D (Unix/Linux)
fclose(fp);
Fondamenti di Informatica
32
}
}
16
Esempio 2
z
Stampare a video il contenuto di un file di testo
prova.txt
fp può essere NULL se il
file richiesto non esiste
#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);
Fondamenti di Informatica
fclose(fp);
}
33
Esempio 3
z
È dato un file di testo people.txt le cui righe
rappresentano ciascuna i dati di una persona,
secondo il seguente formato:
z
z
z
z
z
z
z
cognome (al più 30 caratteri)
uno o più spazi
nome (al più 30 caratteri)
uno o più spazi
sesso (un singolo carattere, 'M' o 'F')
uno o più spazi
anno di nascita
Fondamenti di Informatica
34
17
Esempio 3
z
Si vuole scrivere un programma che
z
z
z
z
legga riga per riga i dati dal file
e ponga i dati in un array di persone
(poi svolgeremo elaborazioni su essi)
Un possibile file people.txt:
Rossi Mario M 1947
Ferretti Paola F 1982
Verdi Marco M 1988
Bolognesi Annarita F 1976
...
Fondamenti di Informatica
35
Esempio 3
1) Definire il tipo persona
z Occorre definire una struct adatta a ospitare i dati
elencati:
z
z
z
z
cognome
Æ
nome
Æ
sesso
Æ
anno di nascita Æ
array di 30+1 caratteri
array di 30+1 caratteri
array di 1+1 caratteri
non è la sola scelta
possibile (ma è comoda…)
un intero
typedef struct {
char cognome[31], nome[31], sesso[2];
int anno;
} persona;
Fondamenti di Informatica
36
18
Esempio 3
Poi, nel main:
2) definire un array di persona
3) aprire il file in lettura
int main() {
persona v[DIM];
FILE* f = fopen("people.txt", "r");
apertura in lettura
if (f==NULL) {
...
}
...
Fondamenti di Informatica
}
37
Esempio 3
Poi, nel main:
2) definire un array di persona
3) aprire il file in lettura
int main() {
persona v[DIM];
perror(msg) stampa
FILE* f = fopen("people.txt", "r"); un messaggio d’errore
sul canale stderr
if (f==NULL) {
perror(“Il file non esiste!”);
exit(1);
exit(n) fa terminare il programma,
}
restituendo al s.o. il valore n come
codice di errore
...
Fondamenti di Informatica
}
38
19
Esempio 3
Poi, nel main:
4) leggere una riga per volta, e porre i dati di
quella persona in una cella dell'array
z
Come organizzare la lettura?
z
z
Dobbiamo leggere delle stringhe separate una dall'altra da
spazi
Sappiamo che ogni singola stringa (cognome, nome,
sesso) non contiene spazi
Scelta più pratica:
Fondamenti di Informatica
fscanf
39
Esempio 3
z
Cosa far leggere a fscanf?
z
z
z
Tre stringhe separate una dall'altra da spazi
z si ripete tre volte il formato %s
Un intero
Æ si usa il formato %d
Il fine riga
Æ occorre specificare in fondo \n
fscanf(f, "%s%s%s%d\n", …)
z
Fino a quando si deve leggere?
z
z
Quando il file termina, fscanf restituisce EOF
z basta controllare il valore restituito
Si continua fintanto che è diverso da EOF
while(fscanf(…)!=EOF)
Fondamenti di Informatica
...
40
20
Esempio 3
z
Dove mettere quello che si legge?
z
z
z
Abbiamo definito un array di persona, v
Struttura fatta di cognome, nome, sesso, anno
Æ ciò che si estrae da una riga va nell'ordine in
v[k].cognome, v[k].nome, v[k].sesso,
v[k].anno
E dopo aver letto una riga?
z
z
La testina di lettura sul file è già andata a capo, perché il
formato di lettura prevedeva esplicitamente di consumare il
fine linea (\n)
L'indice k invece indica ancora la cella appena occupata
Æ occorre incrementarlo, affinché indichi la prossima cella
Fondamenti di Informatica
41
libera
Esempio 3
Poi, nel main:
4) leggere una riga per volta, e porre i dati di
quella persona in una cella dell'array
int main() {
int k=0; /* indice per array */
...
while(fscanf(f,"%s%s%s%d\n",
v[k].cognome, v[k].nome,
v[k].sesso, &v[k].anno ) != EOF){
k++; /* devo incrementare k */
Ricorda: lintero richiede l’estrazione
}
esplicita dell’indirizzo della variabile
Fondamenti di Informatica
}
42
21
Esempio 3
Poi, nel main:
4) leggere una riga per volta, e porre i dati di
quella persona in una cella dell'array
Ricordare:
z fscanf elimina automaticamente gli spazi che separano una
stringa dall'altra, quindi…
z non si devono inserire spazi nella stringa di formato
z
fscanf considera finita una stringa al primo spazio che trova,
quindi…
z non si può usare questo metodo per leggere stringhe contenenti
spazi
Fondamenti di Informatica
43
Esempio 3: programma completo
#define DIM 30
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char cognome[31], nome[31], sesso[2];
int anno;
} persona;
int main() {
persona v[DIM]; int k=0; FILE* f;
if ((f=fopen("people.txt", "r"))==NULL) {
perror("Il file non esiste!");
exit(1); }
while(fscanf(f,"%s%s%s%d\n", v[k].cognome,
v[k].nome, v[k].sesso, &v[k].anno ) != EOF)
k++;
Fondamenti di Informatica
44
}
22
Esempio 3: variante
z
E se usassimo un singolo carattere per rappresentare il sesso?
z
Non più:
typedef struct {
char cognome[31], nome[31], sesso[2];
int anno;
} persona;
z
Ma:
typedef struct {
char cognome[31], nome[31], sesso;
int anno;
} persona;
Fondamenti di Informatica
45
Esempio 3: variante
z
Cosa cambierebbe?
z
fscanf elimina automaticamente gli spazi prima di
leggere una stringa o un numero (intero o reale)...
ma non prima di leggere un singolo carattere,
perché se lo facesse non riuscirebbe a leggere il
carattere spazio
Ma noi non sappiamo quanti spazi ci sono fra
nome e sesso
Quindi, non possiamo sapere a priori dov'è il
carattere che ci interessa
z
z
Fondamenti di Informatica
46
23
Esempio 3: variante
z
Infatti, il nostro file potrebbe essere fatto così:
Rossi Mario M 1947
Ferretti Paola F 1982
Verdi Marco
M 1988
Bolognesi Annarita F 1976
...
z
z
Qui uno spazio prima di M
Qui due spazi prima di F
Qui tre spazi prima di M
Qui uno spazio prima di F
prima, dicendo a fscanf si leggere una stringa, gli
spazi (uno, due, tre..) erano eliminati comunque
adesso, dicendo a fscanf si leggere un carattere
singolo, dobbiamo decidere noi cosa fare
Fondamenti di Informatica
47
Esempio 3: variante
Due possibilità:
z scelta 1: introdurre comunque una stringa
di due caratteri e usarla per far leggere il
carattere relativo al sesso a fscanf
Poi, copiare il primo carattere al suo posto
z
scelta 2: costruirsi un ciclo che salti tutti gli
spazi fino al primo carattere non-spazio, poi
recuperare quest'ultimo…
z
non consente più di usare fscanf per gestire tutta la fase di
lettura
Fondamenti di Informatica
48
24
Variante 1
...
typedef struct {
char cognome[31], nome[31], sesso;
Stringa ausiliaria
int anno;
} persona;
int main() {
persona v[DIM]; int k=0; FILE* f; char s[2];
if ((f=fopen("people.txt", "r"))==NULL) {
perror("Il file non esiste!");
exit(1);
}
while(fscanf(f,"%s%s%s%d\n", v[k].cognome,
v[k].nome, s, &v[k].anno ) != EOF){
v[k].sesso = s[0];
Copiatura caratteri
k++;
Fondamenti di Informatica
}
49
Variante 2
...
typedef struct {
char cognome[31], nome[31], sesso;
int anno;
} persona;
int main() {
persona v[DIM]; int k=0; FILE* f; char ch;
if ((f=fopen("people.txt", "r"))==NULL) {
perror("Il file non esiste!");
exit(1);
}
while(fscanf(f,"%s%s", v[k].cognome,v[k].nome) != EOF){
while((ch=fgetc(f))==' ');
v[k].sesso = ch;
fscanf(f,"%d\n",&v[k].anno);
k++;
Fondamenti di Informatica
}
}
50
25
Variante 2: alternativa
...
typedef struct {
char cognome[31], nome[31], sesso;
int anno;
} persona;
int main() {
persona v[DIM]; int k=0; FILE* f; char ch;
if ((f=fopen("people.txt", "r"))==NULL) {
perror("Il file non esiste!");
exit(1);
}
while(fscanf(f,"%s%s", v[k].cognome,v[k].nome) !=
EOF){
do fscanf(f,”%c”, &ch); while (ch==‘ ‘);
v[k].sesso = ch;
fscanf(f,"%d\n",&v[k].anno);
k++;
Fondamenti di Informatica
}
}
51
Esempio 4
z
È dato un file di testo elenco.txt le cui righe
rappresentano ciascuna i dati di una persona,
secondo il seguente formato:
z
z
z
z
z
z
cognome (esattamente 10 caratteri)
nome (esattamente 10 caratteri)
sesso (esattamente un carattere)
anno di nascita
I primi due possono contenere spazi al loro interno
NB: non sono previsti spazi espliciti di
separazione
Fondamenti di Informatica
52
26
Esempio 4
Cosa cambia rispetto a prima?
sappiamo esattamente dove iniziano e dove
finiscono i singoli campi
z non possiamo sfruttare gli spazi per separare
cognome e nome
Un possibile file elenco.txt:
I vari campi possono essere
z
Rossi
Ferretti
De Paoli
Verdi
Bolognesi
...
Mario
M1947
Paola
F1982
Gian MarcoM1980
Marco
M1988
Annarita F1976
attaccati: tanto, sappiamo a
priori dove inizia l’uno e
finisce l’altro
Fondamenti di Informatica
53
Esempio 4
z
Come fare le letture?
z
z
z
z
non possiamo usare fscanf(f,"%s",…)
si fermerebbe al primo spazio
potrebbe leggere più caratteri del necessario
(si pensi a Gian MarcoM1988)
però possiamo usare fscanf nell'altra modalità,
specificando quanti caratteri leggere.
Ad esempio, per leggerne dieci:
fscanf(f,"%10c",…)
z
Così legge esattamente 10
caratteri, spazi inclusi
Fondamenti di Informatica
54
27
Esempio 4
z
ATTENZIONE:
fscanf(f,"%10c",…)
z
In questo modo viene riempito un array di caratteri,
senza inserire alcun terminatore di stringa.
Occorre aggiungerlo a parte
v[k].nome[10] = ‘\0’;
Fondamenti di Informatica
55
Esempio 4: programma completo
...
typedef struct {
char cognome[11], nome[11], sesso; int anno;
} persona;
int main() {
persona v[DIM]; int k=0; FILE* f;
if ((f=fopen("elenco.txt", "r"))==NULL) {
perror("Il file non esiste!");
exit(1);
}
while(fscanf(f,"%10c%10c%c%d\n", v[k].cognome,
v[k].nome, &v[k].sesso, &v[k].anno ) !=
EOF){
v[k].cognome[10] = ‘\0’;
v[k].nome[10] ='\0';
k++;
Fondamenti di Informatica
}
}
56
28
File binari
z
Un file binario è una pura sequenza di byte, senza
alcuna strutturazione particolare
z
È un'astrazione di memorizzazione assolutamente
generale, usabile per memorizzare su file
informazioni di qualsiasi natura
z
z
z
z
z
"fotografie" della memoria
rappresentazioni interne binarie di numeri
immagini, audio, musica, ….
...volendo, anche caratteri
I file di testo non sono indispensabili:
sono semplicemente comodi
Fondamenti di Informatica
57
File binari
z
Un file binario è una sequenza di byte
z
Può essere usato per archiviare su memoria di
massa qualunque tipo di informazione
Input e output avvengono sotto forma di una
sequenza di byte
La fine del file è rilevata in base all’esito delle
operazioni di lettura
z
z
z
z
non c'è EOF, perché un file binario non è una sequenza di
caratteri
qualsiasi byte si scegliesse come marcatore, potrebbe sempre
capitare nella sequenzaFondamenti di Informatica
58
29
File binari
z
Poiché un file binario è una sequenza di byte,
sono fornite due funzioni per leggere e
scrivere sequenze di byte
z
z
z
fread() legge una sequenza di byte
fwrite() scrive una sequenza di byte
Essendo pure sequenze di byte, esse non
sono interpretate.
Quindi, possono rappresentare qualunque
informazione (testi, numeri, immagini…)
Fondamenti di Informatica
59
Output binario: fwrite()
Sintassi:
int fwrite(addr, int dim, int n, FILE *f);
z
scrive sul file n elementi, ognuno grande dim byte
(complessivamente, scrive quindi n×dim byte)
z
gli elementi da scrivere vengono prelevati dalla memoria a
partire dall’indirizzo addr
z
restituisce il numero di elementi (non di byte) effettivamente
scritti, che possono essere meno di n
Fondamenti di Informatica
60
30
Input binario: fread()
Sintassi:
int fread(addr, int dim, int n, FILE *f);
z
legge dal file n elementi, ognuno grande dim byte
(complessivamente, tenta di leggere quindi n×dim byte)
z
gli elementi da leggere vengono scritti in memoria a partire
dall’indirizzo addr
z
restituisce il numero di elementi (non di byte) effettivamente
letti, che possono essere meno di n se il file finisce prima.
z
Controllare il valore restituito è il solo modo per sapere cosa è stato
letto, e in particolare per scoprire se il file è finito
Fondamenti di Informatica
61
Esempio 1
z
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à:
}
Fondamenti di Informatica
62
la dimensione di int non è fissa
31
Esempio 2
z
Leggere da un file binario numeri.dat una sequenza
di interi, scrivendoli in un array
fread tenta di leggere 40
interi, ne legge meno se il
file finisce prima
#include <stdio.h>
#include <stdlib.h>
int 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++)
n contiene il numero di
printf("%d ",vet[i]);
Fondamenti di Informatica
interi effettivamente63letti
fclose(fp);
}
Esempio 3
z
Scrivere su un file di caratteri testo.txt una sequenza di caratteri
Dopo averlo creato, provare ad aprire
#include <stdio.h>
questo file con un editor di testi
#include <stdlib.h>
qualunque (… e il terminatore?)
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, 1, fp);
fclose(fp);
}
Un carattere in C ha sempre size 1
Scelta: salvare anche il terminatore
Fondamenti di Informatica
64
32
Esempio 4
z
Leggere da un file di caratteri testo.txt una
sequenza di caratteri, ponendoli in una stringa
#include <stdio.h>
#include <stdlib.h>
main(){
FILE *fp;
char msg[80], n;
if ((fp = fopen("testo.txt","rb"))==NULL)
exit(1); /* Errore di apertura */
n = fread(msg, 1, 80, fp);
puts(msg);
N contiene il numero di char effettivamente letti
(che non ci interessa, perché tanto c’è il terminatore)
fclose(fp);
Fondamenti di Informatica
65
}
Esempio 5: output di numeri
z
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
z
z
Supponiamo che sia int x = 31466;
Che differenza c’è fra:
fprintf(file,"%d", x);
Fondamenti di Informatica
66
fwrite(&x, sizeof(int), 1, file);
33
Esempio 5: output di numeri
z
z
z
Se x è un intero che vale 31466, internamente la sua
rappresentazione è (su 16 bit): 01111010 11101010
fwrite() emette direttamente tale sequenza, scrivendo
quindi i due byte sopra indicati
fprintf() invece emette la sequenza di caratteri ASCII
corrispondenti alla rappresentazione esterna del numero
31466, ossia i cinque byte
00110011 00110001 00110100 00110110 00110110
z
Se per sbaglio si emettessero su un file di testo (o su
video) direttamente i due byte: 01111010 11101010 si
otterrebbero i caratteri corrispondenti al codice ASCII di quei
byte: êz
Fondamenti di Informatica
67
Esempio 6: input di numeri
z
Analogamente, che differenza c’è fra
fscanf(file, "%d", &x); e
fread(&x, sizeof(int), 1, file);
z
nell’ipotesi che il file (di testo) contenga la
sequenza di caratteri “23” ?
Fondamenti di Informatica
68
34
Esempio 6: input di numeri
z
z
fscanf() preleva la stringa di caratteri ASCII
00110010 00110011
che costituisce la rappresentazione esterna
del numero, e la converte nella
corrispondente rappresentazione interna,
ottenendo i due byte:
00000000 00010111
z
che rappresentano in binario il valore ventitre
Fondamenti di Informatica
69
Esempio 6: input di numeri
z
fread() invece preleverebbe i due byte
00110010 00110011
Carattere ‘2’
z
z
Carattere ‘3’
credendoli già la rappresentazione interna di un
numero, senza fare alcuna conversione
Tale modo di agire porterebbe a inserire nella
variabile x esattamente la sequenza di byte sopra
indicata, che verrebbe quindi interpretata come il
numero tredicimilacentosei
Fondamenti di Informatica
70
35
Esempio completo file binario
z
È dato un file di binario people.dat i cui record
rappresentano ciascuno i dati di una persona,
secondo il seguente formato:
z
z
z
z
z
cognome (al più 30 caratteri)
nome (al più 30 caratteri)
sesso (un singolo carattere, 'M' o 'F')
anno di nascita
Si noti che la creazione del file binario deve essere
fatta da programma, mentre per i file di testo può
essere fatta con un text editor
Fondamenti di Informatica
71
Esempio completo file binario
z
È necessario scrivere un programma che lo crei
strutturandolo in modo che ogni record contenga una
typedef struct {
char cognome[31], nome[31], sesso[2];
int anno;
} persona;
z
I dati di ogni persona da inserire nel file vengono
richiesti all’utente tramite la funzione leggiel() che
non ha parametri e restituisce come valore di ritorno
la struct persona letta.
Quindi il prototipo è:
persona leggiel();
Fondamenti di Informatica
72
36
Creazione file binario
persona leggiel(){
persona e;
printf(”Cognome ? ");
scanf("%s", e.cognome);
printf("\n Nome ? ");
scanf("%s",e.nome);
printf("\nSesso ? ");
scanf("%s",e.sesso);
printf("\nAnno nascita ? ");
scanf("%d", &e.anno);
return e;
}
Fondamenti di Informatica
73
Creazione file binario
...
typedef struct {
char cognome[31], nome[31], sesso[2];
int anno;
} persona;
main(){
FILE *f; persona e; int fine=0;
if ((f=fopen(“people.dat”, "wb"))==NULL) exit(1);
while (!fine) {
e=leggiel();
fwrite(&e,sizeof(persona),1,f);
printf("\nFine (SI=1, NO=0) ? ");
scanf("%d", &fine);
}
fclose(f);
Fondamenti di Informatica
}
74
37
Creazione file binario
z
z
L’esecuzione del programma precedente crea il file
binario contenente i dati immessi dall’utente.
Solo a questo punto il file può essere utilizzato
Il file people.dat non è visualizzabile tramite un text
editor: questo sarebbe il risultato
rossi > ÿÿ @ T —8
â3
mario ôÜ _ ôÜ Aw O F _
DÝ M
nuinH2ô1 ô1 ô1
Fondamenti di Informatica
75
Esempio completo file binario
z
Ora si vuole scrivere un programma che
z
z
z
legga record per record i dati dal file
ponga i dati in un array di persone
(poi svolgeremo elaborazioni su essi)
Fondamenti di Informatica
76
38
Esempio completo file binario
1) Definire il tipo persona
z Occorre definire una struct adatta a ospitare i dati
elencati:
z
z
z
z
cognome
Æ
nome
Æ
sesso
Æ
anno di nascita Æ
array di 30+1 caratteri
array di 30+1 caratteri
array di 1+1 caratteri
un intero
non è la sola scelta
typedef struct {
possibile (ma è comoda…)
char cognome[31], nome[31], sesso[2];
int anno;
} persona;
Fondamenti di Informatica
77
Esempio completo file binario
Poi, nel main:
2) definire un array di persona
3) aprire il file in lettura (modalità binaria)
int main() {
persona v[DIM];
FILE* f = fopen("people.dat", "rb");
if (f==NULL) {
perror(“Il file non esiste!”);
exit(1);
}
...
exit(n) fa terminare il programma,
Fondamenti di Informatica
restituendo al s.o.
il valore n come
}
perror(msg) stampa
un messaggio d’errore
sul canale stderr
78
codice di errore
39
Esempio completo file binario
Poi, nel main:
4) leggere i record dal file (non c’è più il concetto di
riga), e porre i dati di quella persona in una cella
dell'array
z
Come organizzare la lettura?
int fread(addr, int dim, int n, FILE *f);
z
z
legge dal file n elementi, ognuno grande dim byte
(complessivamente, tenta di leggere quindi n×dim
byte)
gli elementi da leggere vengono scritti in memoria a
partire dall’indirizzo addr
Fondamenti di Informatica
79
Esempio completo file binario
z
Cosa far leggere a fread?
z
L’intero vettore di strutture: unica lettura per DIM record
fread(v,sizeof(persona),DIM,f)
z
Un record alla volta all’interno di un ciclo
i=0;
while(!feof(f)){
fread(&v[i],sizeof(persona),1,f);
i++;
}
Fondamenti di Informatica
80
40
Esempio completo file binario: variante 1
#define DIM 30
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char cognome[31], nome[31], sesso[2];
int anno;
} persona;
main() {
persona v[DIM]; int i=0; FILE* f;
if ((f=fopen("people.dat", "rb"))==NULL) {
printf("Il file non esiste!"); exit(1); }
while(fread(&v[i],sizeof(persona),1,f)>0){
i++;
Fondamenti di Informatica
}
}
81
Esempio completo file binario: variante 2
#define DIM 30
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char cognome[31], nome[31], sesso[2];
int anno;
} persona;
main() {
persona v[DIM]; int i=0; FILE* f;
if ((f=fopen("people.dat", "rb"))==NULL) {
printf("Il file non esiste!");
exit(1);
}
fread(v,sizeof(persona),DIM,f);
Fondamenti di Informatica
}
82
41
File Binari: feof()
int feof(FILE *fp)
z
controlla se nella precedente operazione di
lettura/scrittura sul file cui fa riferimento fp è stata
raggiunta la fine del file.
z
Restituisce:
z
z
0 se la condizione di fine file non è stata raggiunta
un valore diverso da 0 in caso contrario
Fondamenti di Informatica
83
File: accesso diretto
z
In alcuni casi può risultare utile accedere
direttamente ad un particolare byte all’interno
di un file. Vengono usate le seguenti funzioni
di libreria:
fseek
z ftell
z rewind
z
Fondamenti di Informatica
84
42
File: accesso diretto
int fseek(FILE *fp, long offset, int origin)
z
Consente di spostare arbitrariamente la testina di
lettura/scrittura per un file
z
z
fp : file su cui si sta operando
offset :
dà la posizione, rispetto a origin, a cui portarsi sul file
(è espresso in byte è può assumere valori positivi o negativi)
N.B.: per un file di testo deve valere 0 o un valore restituito da
ftell
z
origin :
dà la posizione rispetto a cui misurare l’offset
Fondamenti di Informatica
85
File: accesso diretto
origin può assumere i seguenti tre valori:
z
z
z
SEEK_SET: indica l’inizio del file
SEEK_CUR: indica la posizione corrente
SEEK_END: indica la fine del file
Fondamenti di Informatica
86
43
File: accesso diretto
long ftell(FILE *fp)
z
Restituisce la posizione corrente nel file
specificato
Fondamenti di Informatica
87
File: accesso diretto
void rewind(FILE *fp)
z
Posiziona la testina di lettura/scrittura
all’inizio del file
z
rewind(f) equivale a fseek(f, 0, SEEK_SET)
Fondamenti di Informatica
88
44