Le liste







struct ntemp{char titolo[100];
                   char *autore;
                   int collocazione;
                   struct ntemp *next ;};

typedef struct ntemp nlibro;





void assegna( nlibro *);

void assegna( nlibro * elemento)
{
      printf("Inserisci il titolo del libro (max 99 char)");
      gets (elemento -> titolo);

      elemento -> autore = (char *) malloc (51 * sizeof(char));
      printf("Inserisci il nome dell'autore (max 50 char) ");
      gets (elemento -> autore);
  
      printf("Inserisci la collocazione del volume");
      scanf("%d", & ( elemento -> collocazione ) );
}





void stampa(nlibro * );

void stampa(nlibro * testa)
{
  while(testa != NULL)
    {printf("La collocazione del libro %s e' %d", testa-> titolo,testa-> collocazione);
      testa = testa -> next;}
}















I file




La gestione dell'input e dell'output in C avviene mediante delle strutture chiamate canali. I canali servono da interfaccia tra il codice ed i dispositivi fisici su cui vengono effettuate le operazioni di lettura e scrittura; in questo modo la complessità della gestione del dispositivo viene lasciata alla funzione che gestisce l'input o l'output da/verso uno specifico dispositivo fisico. Il programmatore, quindi, non si deve preoccupare della gestione del dispositivo ma dovrà però conoscere quali sono le operazioni di lettura/scrittura effettuabili su uno specifico dispositivo fisico (ad esempio, se sto gestendo un CD-ROM e non sto utilizzando un masterizzatore, non posso permettermi di effettuare operazioni di scrittura; oppure, non posso effettuare operazioni di output sulla tastiera ...)


Riassumendo: dall'interno del codice il flusso di informazioni in entrata ed in uscita si gestisce mediante CANALI o FILE. I canali si gestiscono tutti allo stesso modo.






Fino ad ora abbiamo incontrato due canali:

input standard    indicato con    stdin ( per la gestione della tastiera)

output standard    indicato con    stdout (per la gestione del monitor)




Le funzioni printf(), puts(), putchar() inviano segnali al monitor mediante lo stdout mentre scanf(), gets(), getchar() ricevono segnali dalla tastiera mediante lo stdin. Questi due canali vengono automaticamente aperti al momento dell'avvio del programma e vengono chiusi al momento del termine del programma stesso.




In modo analogo, l'accesso al disco si effettua usando canali di input ed output e tali canali possono essere aperti in modalità testo o in modalità binaria.


int prova = 123456789;

in modalità testo -> 9 byte (meglio 10, devo inserire uno spazio per distinguerlo da quanto scrivo dopo ...)

in modalità binaria -> 4 byte    // + economica !



















Apertura e chiusura dei canali:



FILE * fopen(const char* nome_file,const char*modalita)


modalità:

"r"   lettura
"w"  scrittura (cancella il contenuto del file, se esso esiste già)
"a"   append (si inizia a scrivere da fine_file (ovvero EOF))
"r+"  aggiornamento: lettura e scrittura
"w+" aggiornamento (cancella eventuali contenuti preesistenti)
"a+" aggiornamento: si inizia a scrivere da fine_file



Il nome del file può contenere al massimo FILENAME_MAX caratteri ( -> 260) e si possono aprire al massimo FOPEN_MAX file ( -> 20)


NOTA:

(1) Il tipo di dato FILE è una struttura (v. stdio.h) che contiene informazioni relative allo specifico dispositivo cui stiamo accedendo (nel caso stessimo leggendo un file su disco, ad esempio, il nome del file, la sua dimensione, in quale punto del file stiamo leggendo ...)

(2) le operazioni di output ed input sono gestite mediante un buffer. In particolare, le operazioni di scrittura vengono immediatamente effettuate solo sul buffer non sul file nel quale stiamo scrivendo. Per forzare la scrittura su file:



int fflush(FILE * output)



fflush(NULL) effettua la stessa operazione su tutti i canali in uscita



[ per lo stesso motivo è necessario smontare i dischetti e le chiavette quando si usa il sistema operativo Linux ]
















Per determinare quando sono giunto alla fine del file:

int feof(FILE * puntatore)

La funzione restituisce 1 quando siamo arrivati a fine file (posizione in cui si trova il carattere EOF)


int * fclose(FILE* puntatore)



IMPORTANTE: stdin e stdout non vanno nè aperti nè chiusi !














(1) funzioni per la lettura/scrittura di caratteri

La funzione

int getc(FILE *input)


permette di leggere un singolo carattere da un file (accessibile mediante il puntatore input ); la funzione restituisce un intero pari al codice ASCII del carattere letto (è l'equivalente di getchar())

Viceversa, la funzione

int putc(int carattere,FILE* out)


consente di inviare un singolo carattere ad un file (accessibile mediante il puntatore out ). La funzione restituisce un intero pari al codice ASCII del carattere inviato sul file (è l'equivalente di putchar())








#include <stdio.h> // da includere per la gestione dei file

int main()
{char c;
// la variabile numero rappresenterà il numero di caratteri letti dal file
int numero=0;

// apro un file di testo dal nome testo.dat in modalità lettura
FILE *input=fopen(“testo.dat”,”r”);

/* per determinare quando il file termina, uso la funzione feof(FILE *) che restituisce 0 quando non siamo alla fine del file e restituisce 1 quando ci troviamo alla fine del file. Quindi il ciclo continua fino a quando feof() non restituisce un valore VERO (ovvero un intero positivo) */

while(!feof(input))
{c=getc(input);
  numero++;
  printf(“%c”,c”);}
printf(“il numero di caratteri letti è pari a %d”,numero);
fclose(input);
return 0;}




Nota: la fine del file viene identificata da un carattere -> se effettuo operazioni di lettura carattere per carattere posso riconoscere da solo quando sono arrivato in fondo al file.






#include <stdio.h> // da includere per la gestione dei file

int main()
{char c;
int numero=0;

FILE *input=fopen(“testo.dat”,”r”);

while(1)
{c=getc(input);

  if(c==EOF) break;

  numero++;
  printf(“%c”,c”);}
 printf(“il numero di caratteri letti è pari a %d”,numero);
 fclose(input);
 return 0;}

















Nota: oltre ai canali collegati ai file ci sono anche i canali di input ed output standard !



#include <stdio.h> // da includere per la gestione dei file

int main()
{char c;
int numero=0;

FILE *input=fopen(“testo.dat”,”r”);

while(1)
{c=getc(input);

  if(c==EOF) break;

  numero++;
  putc(c,stdout); }  // e' l'equivalente di putchar(c);

 printf(“il numero di caratteri letti è pari a %d”,numero);
 fclose(input);
 return 0;}

















(2) funzioni per la lettura/scrittura (char, int, float, double)

int fscanf(FILE *canale,char *formato,…);

int fprintf(FILE *canale,char *formato,…);

consentono di leggere/scrivere interi gruppi di caratteri interpretandoli come interi, decimali, caratteri o stringhe (così come facevano printf() e scanf() ). Il significato della stringa formato è lo stesso attribuito alla stringa di formato della funzione scanf() e la stessa cosa vale per la stringa di formato di fprintf() . Il valore restituito da fscanf() rappresenta il numero di variabili lette con successo; fprintf() restituisce il numero di caratteri scritti oppure, in caso di errore, un numero negativo.

















Supponiamo di voler leggere, parola per parola, un file di testo. Visto che le operazioni di input da file vengono gestite mediante un buffer, possiamo immaginare di leggere direttamente da tale buffer, come avveniva durante le operazioni di input da tastiera.




Contenuto del file: monti.txt

Addio, monti sorgenti dall'acque, ed elevati al cielo; cime inuguali, note a chi e' <LF><CR>
cresciuto tra voi, e impresse nella sua mente, non meno che lo sia l'aspetto de' <LF><CR>
suoi più familiari; torrenti, de' quali distingue lo scroscio, come il suono delle <LF><CR>
voci domestiche; ville sparse e biancheggianti sul pendìo, come branchi di <LF><CR>
pecore pascenti; addio! <LF><CR>
 <EOF>


#include <stdio.h>

int main()
{FILE * lettura=fopen("monti.txt","r");

  char temp[100];

  while(! feof(lettura) )
   {fscanf( lettura,"%s",temp);
     fprintf(stdout,"%s",temp);}

 fclose( lettura);
 return 0;}




















Attenzione: dopo la parola "addio!" si va a capo e c'è uno spazio, quindi dopo l'ultima operazione di lettura "l'indice" con cui stiamo leggendo il file non è ancora giunto a fine_file e quindi viene effettuata ancora una operazione di lettura. Tale operazione, però, non essendoci caratteri da leggere, non va a buon fine e quindi nella stringa temp rimangono i caratteri inseriti durante l'ultima lettura (ovvero visualizzo due volte l'ultima riga)


Per evitare questo possiamo sfruttare il valore di ritorno di fscanf()

il valore restituito è pari a
(1) il numero di parole lette
(2) EOF (-> -1) in caso di errore o se incontra la fine del file



#include <stdio.h>

int main()
{FILE * lettura=fopen("monti.txt","r");

  char temp[100];

  while( fscanf( lettura,"%s",temp) > 0 )
   {fprintf(stdout,"%s",temp);}

 fclose( lettura);
 return 0;}





Se le cose son state fatte in modo corretto, questo è l'output sul monitor


/home/studente/programmi> myprog
Addio, monti sorgenti dall'acque, ed elevati al cielo; cime inuguali, note a chi e' cresciuto tra voi, e impresse nella sua mente, non meno che lo sia l'aspetto de' suoi più familiari; torrenti, de' quali distingue lo scroscio, come il suono delle voci domestiche; ville sparse e biancheggianti sul pendìo, come branchi di pecore pascenti; addio!
/home/studente/programmi>


























(2) funzioni per la lettura/scrittura di intere righe di testo

Le funzioni fgets() e fputs() consentono di leggere intere righe (o porzioni di esse) all'interno di un file accessibile mediante un canale di testo. I prototipi delle funzioni sono

char *fgets(char *stringa,int numero_caratteri,FILE *input);

char *fputs(char *stringa,FILE *output);


IMPORTANTE: fgets() restituisce NULL se incontra la fine del file o se genera un errore; la funzione inserisce nella stringa su cui scrive i caratteri letti dal file anche il carattere "a capo"; dopo averlo inserito, aggiunge il carattere di terminazione















Contenuto del file: monti.txt

Addio, monti sorgenti dall'acque, ed elevati al cielo; cime inuguali, note a chi e' <LF><CR>
cresciuto tra voi, e impresse nella sua mente, non meno che lo sia l'aspetto de' <LF><CR>
suoi più familiari; torrenti, de' quali distingue lo scroscio, come il suono delle <LF><CR>
voci domestiche; ville sparse e biancheggianti sul pendìo, come branchi di <LF><CR>
pecore pascenti; addio! <LF><CR>
 <EOF>


#include <stdio.h>

int main()
{FILE * lettura=fopen("monti.txt","r");

  char temp[100];

  while(! feof(lettura) )
   {fgets(temp,100,lettura);     // funziona meglio di gets() !
     fprintf(stdout,"%s",temp);}  

 fclose( lettura);
 return 0;}







Se le cose sono state fatte in modo corretto:

/home/studente/programmi> myprog
Addio, monti sorgenti dall'acque, ed elevati al cielo; cime inuguali, note a chi e'
cresciuto tra voi, e impresse nella sua mente, non meno che lo sia l'aspetto de'
suoi più familiari; torrenti, de' quali distingue lo scroscio, come il suono delle
voci domestiche; ville sparse e biancheggianti sul pendìo, come branchi di
pecore pascenti; addio!
/home/studente/programmi>
















Ora ritorniamo chiudiamo tornando alle liste ... supponiamo di voler leggere i dati da un file





// inizio codice

#include <stdlib.h>
#include <stdio.h>
#include "myfun.h"
   // in questo file posso dichiarare il prototipo di struttura


int main()
{// inizializzo SUBITO i puntatori !!
   nlibro * testa = NULL;
   nlibro * da_inserire = NULL;
 
   FILE * input=fopen("elenco_autori.txt");
   FILE * output = fopen("nuovo_elenco_autori.txt");

  while( !feof(input) )
   { 
     da_inserire = nuovo();
     assegna(da_inserire, input );
     testa=aggancia(testa,da_inserire);
    }
 stampa(testa,output);

 return 0;}
// fine codice









void assegna( nlibro *, FILE *);

void assegna( nlibro * elemento,FILE * lettura )
{
      fgets (elemento -> titolo,100,lettura);

      elemento -> autore = (char *) malloc (51 * sizeof(char));
      fgets (elemento -> autore,50,lettura);   

      fscanf(lettura,"%d", & ( elemento -> collocazione ) );
}







void stampa(nlibro * , FILE *);

void stampa(nlibro * testa, FILE * output)
{
  while(testa != NULL)
    { fprintf(output,"La collocazione del libro %s e' %d", testa-> titolo,testa-> collocazione);
      testa = testa -> next;}
}