INPUT/OUTPUT (con conversione di formato)

Transcript

INPUT/OUTPUT (con conversione di formato)
SOMMARIO SU
LETTURA E SCRITTURA (I/O) DEGLI STANDARD FILES
•
Con conversione di formato:
int scanf (const char *format,... )
/* ritorna il numero di argomenti assegnati con successo altrimenti
EOF se fine file o errore */
int printf (const char *format,... )
/* ritorna il numero di caratteri scritti o EOF se errore*/
•
Carattere per carattere:
int getchar(void)
/* ritorna il prossimo carattere di input come un unsigned char
(convertito ad int), altrimenti EOF */
int putchar(int c)
/* scrive c convertito ad unsigned char; ritorna il carattere scritto
oppure EOF in caso di errore */
•
Linea per linea:
char *gets(char *line)
/* legge la prossima linea di input dentro l'array line
rimpiazzando '\n' con '\0'; ritorna line oppure NULL se si
determina fine file o errore */
int puts(const char *line)
/* scrive i caratteri in line (fino a '\0' escluso) e aggiunge
'\n'; ritorna EOF se si determina errore, zero altrimenti */
Unix ha tre standard files associati ad ogni terminale:
standard input (per default la tastiera)
standard output (per default il video)
standard error (per default il video)
denominato stdin
denominato stdout
denominato stderr
che possiamo ridefinire così:
comando di input (es. a.out)
comando di output (es. a.out)
comando di output (es. a.out)
comando di output (es. a.out)
comando di output (es. a.out)
< file
> file
> > file
2 > file
& > file
input preso da file
output diretto a file
output appeso al file
error sul file
output ed error sul file
INPUT/OUTPUT (con conversione di formato)
int printf (const char *format, ... );
La notazione ...
indica che il numero dei parametri è variabile.
/* ritorna il numero di caratteri scritti o EOF se errore */
/* la stringa format contiene sia caratteri ordinari, che vengono
trascritti tali quali sia specifiche di conversione */
/* ogni specifica di conversione inizia con % e finisce con un carattere di
conversione: altre opzioni sono:
%
- amp . prec
char_conv
amp è:
ampiezza minima del campo (riempito di solito da destra, a meno che
sia preceduto dal segno - che indica riempimento da sinistra )
prec è:
per un int è il min num di cifre di stampa
per un float è il num di cifre decimali dopo il .
per una stringa di char è il max num di caratteri
( U n *.* al posto di amp . prec assegna ad amp e prec i due
prossimi argomenti, che devono essere interi )
Un
con conversione
viene stampato come
----------------------------------------------------------------------------------------%d , %i
decimale con segno
%o
ottale senza segno
int(32)
%x , %X
esadecimale senza segno
[o char(8)]
%u
decimale senza segno
%c
carattere (ultimo byte)
%f
double (64)
%e
[o float (32)] %E
%g,%G
[-]m.dddddd
[-]d.dddddde+xx
[-]d.ddddddE+xx
il meglio tra i tre precedenti
char
void
tutti i caratteri fino a '\0'
un indirizzo
*
*
%s
%p
int scanf (const char *format, ... );
ritorna il numero di argomenti assegnati con successo oppure EOF.
/* ogni argomento deve essere un pointer */
/* nel format blanks e tabs vengono ignorati; gli altri caratteri (diversi
da %) devono invece combaciare perfettamente con i caratteri non white
dell'input. I caratteri white sono: blank, tab, newline, carriage return,
vertical tab, form feed */
/* ogni specifica di conversione inizia con %, finisce con un carattere di
conversione e guida la conversione del prossimo campo di input. Un
campo di input inizia con un non-white e si estende fino alla fine dei nonwhite o del numero di caratteri specificati da amp; unica eccezione è la
lettura di un carattere. In pratica la lettura dei numeri termina al primo
carattere non lecito, la lettura delle stringhe al primo carattere white. Le
opzioni sono:
%
*
amp
*
amp
char_conv
per saltare l'assegnazione (ed il campo di input)
max campo di input
Un
con conversione
deve leggere un
---------------------------------------------------------------------------------------%d
decimale
%i
intero (dec. o ott. con 0 o
int *
esadecimale con 0x)
%o
ottale ( scritto con o senza 0)
%x
esad. (scritto con o senza 0x)
unsigned int*
%u
intero dec senza segno
char
%c
%1s
%s
il primo carattere
il primo carattere non white
tutti i caratteri del campo
float
*
*
double *
%e, %f, %g
foating point
%le, %lf, %lg
foating point
Oss: un double si legge (senza differenza) con lf, le, lg ma si
stampa (con differenza) con f, e, g .
SOMMARIO SU
LETTURA E SCRITTURA (I/O) DI FILES NON STANDARD
•
Con conversione di formato:
int fscanf (File *fp, char *format,... )
/* come scanf, con la differenza che legge dal file fp */
int fprintf (File *fp, char *format,... )
/* come printf, con la differenza che scrive sul file fp */
•
Carattere per carattere:
int getc (File *fp)
/* come getchar, con la differenza che legge dal file fp */
int putc (int c, File *fp)
/* come putchar, con la differenza che scrive sul file fp */
int ungetc (int c, FILE *fp)
/* rimette il carattere c sul file fp e ritorna c, o EOF se errore */
• Linea per linea:
char *fgets(char *line,int maxline, FILE
*fp)
/* legge al più maxline-1 caratteri dentro line, terminando prima se incontra
'\n'; il carattere '\n' è incluso nell'array che è terminata con '\0' ; ritorna
line oppure NULL se incontra end of file o errore */
int fputs(const char *line, FILE
*fp)
/* scrive line (che non deve includere necessariamente '\n') sul file fp ;
ritorna 0 oppure EOF se c'è errore */
• Blocco per blocco:
size_t fread(void *ptr,size_t size,size_t nobj,FILE *fp)
size_t fwrite(void *ptr,size_t size,size_t nobj,FILE *fp)
Oss. Ad eccezione delle funzioni fscanf() e fprintf(), che hanno
fp come primo parametro, le altre lo hanno come ultimo parametro.
Su un file non standard aperto si scrive e si legge in modo simile a
come si scrive o si legge sugli standard files.
Esempio 1:
#include <stdio.h>
#include<stdlib.h>
main()
{
char parola[30];
FILE *fp;
if ((fp = fopen("nuovo","w")) == NULL) {
puts("errore in apertura per scrivere");
exit(1);
};
fprintf(fp,"Oggi e' il 4 dicembre\n");
fclose(fp);
if ((fp = fopen("nuovo","r")) == NULL) {
puts("errore in apertura per leggere");
exit(1);
}
while (fscanf(fp,"%s", parola) != EOF)
puts(parola);
/* qui feof(fp) è diventata vera */
fclose(fp);
return 0;
}
Stampa:
Oggi
e'
il
4
dicembre
GESTIONE DI FILES
LETTURA E SCRITTURA (I/O) DI FILES NON STANDARD
Sappiamo che quando un programma inizia, tre standard files vengono
aperti automaticamente: stdin, stdout e stderr.
Per poter leggere (o scrivere) un file non standard occorre aprirlo con
la funzione FILE *fopen() in questo modo:
FILE *fp;
fp = fopen ("path_name_del_file", "r");
/* if (fp == NULL)
exit(1); */
La funzione fopen() ritorna NULL in caso di errore, altrimenti apre il
file di nome path_name_del_file, lo associa ad un flusso (stream)
di caratteri, memorizza in una struct FILE le informazioni su file e
flusso e ritorna un pointer ad essa. Con "w" ( risp. "a") si apre il file per
scrivere (risp. aggiungere) dati.
L a struct FILE è definita in <stdio.h> ed è costituita da molti campi che
contengono informazioni quali:
nome del file
modalità di accesso
indicatore di posizione nel file
indicatore di eof
indicatore di error
L ' indicatore di posizione nel file punta al successivo byte da leggere o scrivere: a
fronte di operazioni di lettura/scrittura, il sistema operativo modifica l’indicatore di
posizione. Esso non può essere manipolato direttamente, ma può essere letto e
modificato tramite funzioni di libreria.
In C il termine file può riferirsi a un file su disco, al monitor, alla tastiera,
a una porta, ecc. Mentre i files differiscono tra loro, gli “streams” sono
uguali, cioè sono sequenze di bytes. Lo stream è quindi interfaccia
uniforme ai files. Così l'I/O relativo ad un file diventa I/O di bytes di
uno stream.
Se si cerca di aprire un file inesistente per scrivere o aggiungere dati, il
S.O. lo crea e scrive su di esso i dati, mentre se si cerca di aprire un file
inesistente per leggerlo fopen ritorna NULL.
Buona norma è chiudere un file aperto, se non serve più, con:
fclose (fp);
Comunque i files aperti vengono automaticamente chiusi quando un
programma termina normalmente.
Ci sono due tipi di streams:
text stream
Un text stream (flusso testuale) è composto da una sequenza di linee,
concluse da newline. Sistemi operativi diversi possono memorizzare
linee con formati diversi, utilizzando ad esempio caratteri differenti di
terminazione linea.
(è usato con caratteri ASCII; può non esserci una corrispondenza oneto-one tra stream e ciò che c’è nel file poichè qualche “character
translation” può avvenire)
binary stream
Su un binary stream (flusso binario) il compilatore non effettua alcuna
interpretazione dei byte: i bits di un byte sono letti e scritti come un
flusso continuo.
(è usato quando è fondamentale preservare l’esatto contenuto del file;
nessun “character translation”; c’è corrispondenza one-to-one tra
stream e file).
Di conseguenza ci sono vari modi per aprire un file.
Mode
r
w
a
Meaning
Open a text file for reading
Create a text file for writing
Append to a text file
r+
Open a text file for read/write
w+
Create a text file for read/write
a+
Append or create a text file for read/write
----------------------------------------------------------rb
Open a binary file for reading
wb
Open a binary file for writing
ab
Append to a binary file
r+b
w+b
a+b
Open a binary file for read/write
Create a binary file for read/write
Append a binary file for read/write
Funzioni utili
La chiamata feof(fp) ritorna un valore vero se si è raggiunta la fine
del file associato con fp altrimenti ritorna 0 (su files binari feof(fp)
diventa vera dopo la lettura dell’ultimo dato scritto).
La chiamata ferror(fp) ritorna un valore vero se durante la gestione
del file associato con fp si è verificato un errore, altrimenti ritorna 0.
La chiamata clearerr(fp) riporta ai valori di default i campi
error ed eof della struct FILE del file associato con fp.
La chiamata rewind(fp) posiziona la posizione corrente all’inizio
del file.
Infine la chiamata exit (int) fa terminare l'esecuzione del
programma. L'argomento di exit può essere utilizzato dal processo
chiamante ed usato per stabilire se la chiamata ha avuto successo oppure
no. Di solito 0 sta per "tutto bene" mentre un valore diverso da zero
indica "errore".
Osservazioni
Il file stdio.h contiene quindi:
- Le dichiarazioni dei prototipi di tutte le funzioni di I/O
- Le macro costanti EOF, stdin, stdout, stderr
- La dichiarazione della struct FILE
EOF corrisponde al valore restituito da alcune funzioni di I/O in
corrispondenza dell’identificatore di fine file.
La definizione di NULL, per l’ANSI C, è invece contenuta nel file
stddef.h:
#ifndef NULL
#define NULL (void *) 0
#endif
Le combinazioni di chiavi per indicare end-of-file varia tra i sistemi:
UNIX
IBM PC
MACINTOSH
<return> <ctrl > d
<ctrl > z
<ctrl > d
NUOVE FUNZIONI di I/O
(I/O blocco per blocco)
(solo per Files Binari)
Le seguenti funzioni possono leggere e scrivere qualunque tipo di dato,
qualunque sia la sua rappresentazione. I prototipi sono:
size_t fwrite(void *buffer,
size_t size, size_t num, FILE *fp);
size_t fread (void *buffer,
size_t size, size_t num, FILE *fp);
La funzione fwrite() scrive sul file associato ad fp, un numero num
di oggetti, ciascuno lungo size bytes, prendendoli dall’area puntata da
buffer. Ritorna il numero di oggetti scritti. Questo valore sarà diverso
da num solo in caso di errore .
La funzione fread() legge dal file associato ad fp, un numero num
di oggetti, ciascuno lungo size bytes, e li memorizza nel buffer puntato
d a buffer. Ritorna il numero di oggetti letti; se ritorna 0, nessun
oggetto è stato letto, cioè la fine del file è stata raggiunta oppure c’è stato
errore.
Esempio:
#include <stdio.h>
#include <stdlib.h>
int main()
{
float f = 1, g, h;
float a[3] = {2.,3.,4.}, b[2];
FILE *fp;
if ((fp = fopen("nuovo","wb"))==NULL) {
fputs("errore in apertura file",stderr);
exit(1);
}
fwrite(&f, sizeof(float), 1, fp);
fwrite(a, sizeof(a), 1, fp);
if (ferror(fp)) {
puts("errore in scrittura file");
exit(1);
}
fclose(fp);
if ((fp = fopen("nuovo","rb"))==NULL) {
puts("non posso aprire file scritto");
exit(1);
}
fread(&g, sizeof(float), 1, fp);
fread(b, sizeof(b), 1, fp);
fread(&h, sizeof(float), 1, fp);
if (ferror(fp)) {
puts("errore in lettura");
exit(1);
}
if (feof(fp)) {
puts("lettura oltre la fine file");
exit(1);
}
printf("%f %f %f %f\n",g, b[0],b[1], h);
fclose(fp);
return 0;
}
E stampa:
1.000000 2.000000 3.000000 4.000000
BUFFERIZZAZIONE
Confrontate con la memoria centrale, le unità di memoria di massa sono
molto più lente; il tempo richiesto per accedere alle periferiche eccede
largamente il tempo impiegato dalla CPU per i calcoli effettivi. È quindi
di fondamentale importanza ridurre, mediante tecniche di bufferizzazione,
il numero di accessi alla memoria di massa per effettuare operazioni di
lettura/scrittura. Un buffer è un’area dei memoria in cui i dati sono
memorizzati temporaneamente, prima di essere inviati alle unità di I/O o
dopo essere ricevuti dalle unità.
Tutti i sistemi operativi utilizzano buffer per leggere/scrivere su unità di
I/O: l’accesso avviene con “granularità di blocco”, con blocchi di
dimensione 512/4096 byte.
Le librerie run−time del C contengono due forme distinte di
bufferizzazione: bufferizzazione a blocchi e bufferizzazione a linee.
Nella bufferizzazione a blocchi, il sistema immagazzina i caratteri fino a
riempire un blocco, trasferendolo quindi al sistema operativo.
Nella bufferizzazione a linee, il sistema immagazzina i caratteri fino a
quando incontra un newline (oppure il buffer è pieno), poi invia l’intera
linea al sistema operativo (così accade per l’inserimento da tastiera).
Tutti i flussi di I/O a file utilizzano una bufferizzazione a blocchi, mentre
i flussi riferiti a terminale sono dipendenti dal sistema operativo, e sono o
non bufferizzati o bufferizzati a linee.
Sia nel caso di bufferizzazione a linee che a blocchi, è possibile richiedere
esplicitamente al sistema operativo di forzare l’invio del buffer a
destinazione in un momento qualsiasi, per mezzo della funzione
fflush().
ACCESSO DIRETTO A FILES
Si fa con le funzioni: ftell() e fseek(), usando le costanti
seguenti che sono definite in stdio.h:
SEEK_SET
SEEK_CUR
SEEK_END
indica inizio del file
indica posizione corrente del file
indica fine del file.
La funzione ftell() ha, come unico argomento, un puntatore ad un file
e restituisce la posizione corrente dell’indicatore di posizione nel file.
La posizione restituita da ftell() si intende relativa all’inizio del file...
….. per flussi binari rappresenta il numero di caratteri dall’inizio del file
alla posizione corrente;
….. per flussi testuali è un valore dipendente dall’implementazione,
significativo solo se utilizzato come parametro per fseek().
La funzione fseek() sposta l’indicatore di posizione del file a un
carattere specificato del flusso.
Es.
/* Se la ricerca di una certa stringa nel file fallisce,
l’indicatore di posizione nel file viene riportato al valore
originale */
cur_pos = ftell(fp);
// posizione originale
if (search(string) == FAIL)
fseek(fp, cur_pos, SEEK_SET);
// riporta l’indicatore nella posizione originale
Il prototipo di fseek() è:
int fseek(FILE *fp, long int offset, int da_dove)
dove:
fp
offset
da_dove
: puntatore a file
: numero di caratteri di spostamento
: posizione di partenza da cui calcolare lo spostamento
L’argomento da_dove può assumere uno dei tre valori SEEK_SET,
SEEK_CUR, SEEK_END.
La funzione fseek() restituisce zero se la richiesta è corretta, un valore
diverso da zero altrimenti.
Es: L’istruzione
stat = fseek(fp, 10, SEEK_SET);
sposta l’indicatore di posizione del file dopo 10 caratteri del flusso
(sull’undicesimo), che sarà il prossimo elemento letto o scritto.
Per flussi binari, lo spostamento (offset) può essere un qualsiasi
numero intero che non sposti l’indicatore al di fuori del file; per flussi
testuali, deve essere zero o un valore restituito da ftell().
Es: L’istruzione
stat = fseek(fp, 1, SEEK_END);
non è lecita se fp è aperto in sola lettura, perché sposta l’indicatore oltre
la fine del file.
NOTA (da manuale): tra una operazione di fread ed una di fwrite (o
viceversa) occorre effettuare una chiamata di fseek oppure di fflush.
Es:
/* Determinazione con fseek e ftell del numero di caratteri di
un file passato come parametro */
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *fp;
int n;
if (argc < 2)
printf("File non specificato\n");
else {
if (( fp = fopen (argv[1], "r")) == NULL){
printf("Non trovo il file %s\n", argv[1]);
exit(1);
}
/* spostamento puntatore alla fine del file */
fseek(fp, 0, SEEK_END);
/* lettura della posizione dell'indicatore */
n = ftell(fp);
printf("Il file ha %d caratteri\n",n);
fclose(fp);
}
return 0;
}