La funzione fseekin C consente di muovere il puntatore di lettura e/o scrittura in una posizione qualunque all’interno del file, dopo la sua apertura con fopen.
Finora l’accesso al file è stato di tipo sequenziale, ovvero per aggiungere o leggere un elemento ci siamo posizionati esattamente sotto tutti gli elementi precedenti.
Infatti, molte volte c’è la necessità di realizzare un accesso diretto o random ad un elemento di un file. Ecco dunque che la funzione fseek ritorna molto utile a questo scopo.
La sintassi della funzione fseek in C è quindi la seguente:
int fseek(FILE *fp, long distanza, int partenza)
Dove fp è il file pointer, distanza indica di quanti byte il file pointer deve essere spostato mentre partenza indica da quale posizione deve essere spostato.
La funzione fseek ritorna 0 se lo spostamento è avvenuto con successo, altrimenti un valore diverso da 0.
Allora definiamo il punto di partenza che può assumere solo uno di questi tre valori:
Realizziamo dunque lo stesso esempio di prima con rewind con la funzione fseek in C, solo a scopo didattico per capirne il funzionamento.
#include<stdlib.h>
#include<stdio.h>
#define N 100
int main() {
FILE *fp;
char buf[N];
if((fp=fopen("alunni.txt", "rt"))==NULL) {
printf("Errore nell'apertura del file'");
exit(1);
}
/* legge e stampa fino alla fine del file */
while(!feof(fp)) {
fgets(buf,N,fp);
printf("%s", buf);
}
/* riporta il file pointer all'inizio */
fseek(fp,0,SEEK_SET);
printf("\nStampo la seconda volta\n");
/* legge e stampa fino alla fine del file */
while(!feof(fp)) {
fgets(buf,N, fp);
printf("%s", buf);
}
printf("\nStampo la terza volta\n");
fseek(fp,0,SEEK_CUR);
/* legge e stampa fino dalla posizione corrente dell'indicatore che in questo caso è l'ultima riga*/
while(!feof(fp)) {
fgets(buf,N, fp);
printf("%s", buf);
}
fclose(fp);
return 0;
}
Le potenzialità della funzione fseek in C saranno trattate anche nelle prossime lezioni in maniera approfondita.
Proponiamo ancora degli esercizi sui file in C ed in particolare sulle funzioni fopen, fwrite, fread e fclose.
Propongo questo esempio a scopo di introdurre, prossimamente, altre funzioni che ci permetteranno di posizionarci in una posizione qualunque del file e che quindi garantiscono un accesso diretto e non più sequenziale.
Esercizi sui file in C – problema
In un file binario sono memorizzati i dati di un vettore che contiene al massimo 50 elementi. Stampare i dati di questo file, se presenti, altrimenti dare come messaggio: “Nessun dato presente!”. Dopo chiedere quanti dati si vogliono aggiungere al vettore e creare una funzione che consenta di farlo. Creare un’altra funzione per il salvataggio di questi dati nel vettore. Infine ristampare nuovamente i dati del file aggiornati con il nuovo inserimento.
Le funzioni/procedure utilizzate sono dunque 4. Notate come la funzione init deve restituire un intero che corrisponde al numero di elementi che vengono inseriti nel vettore. Mentre le altre procedure non ritornano alcun valore.
Quindi la funzione init ha il compito di aprire in lettura il file binario, utilizzando fopen, e se l’operazione ha successo legge gli elementi in esso presenti utilizzando la funzione fread. Se non ci sono dati verrà stampato un messaggio: Nessun dato presente! Dopo chiudiamo il file binario con la funzione fclose.
La procedura print stampa semplicemente i dati presenti senza restituire alcun valore. Prende come parametri il vettore e il numero degli elementi.
La procedura add ha il compito di aggiungere gli elementi e quindi ha la necessità di prendere come parametri non solo il vettore ma anche il puntatore all’ultimo elemento inserito. Quindi chiediamo quanti elementi inserire e li aggiungiamo a quelli precedenti. Potremmo fare anche un controllo per far in modo che non si inseriscano più elementi rispetto alla dimensione massima dichiarata, che in questo caso è 50.
Infine la procedura save si occupa di salvare i dati inseriti nel file.
Proponiamo oggi alcuni esercizi sui file con vettori in C. Utilizzeremo i file binari e le funzioni fclose, fopen, fread e fwrite.
Esempio sui file con vettori in C
Realizzare un semplice esempio che crea una procedura per l’inserimento dei dati in un vettore e poi la copia di questi dati su file. Creare anche una procedura per la stampa dei dati inseriti.
Innanzitutto chiediamo all’utente quanti numeri vuole inserire (n) e dunque invochiamo la procedura per inserire i dati nel vettore. Facciamo il controllo che n sia positivo e minore di 10 (abbiamo stabilito di voler inserire massimo 10 numeri e abbiamo memorizzato questo valore nella costante N).
Quindi apriamo il file binario vettori.dat in scrittura, con la funzione fopen, e se l’operazione ha successo memorizziamo i dati nel file con la funzione fwrite. Dopo aver fatto ciò chiudiamo il file.
Poi riapriamo lo stesso file binario in lettura, sempre con la funzione fopen, e utilizziamo la funzione fread per leggere i dati dal file. Invochiamo poi la funzione print per stampare i dati del vettore e infine chiudiamo il file.
lLa funzione fread permette quindi di leggere un array da file con una singola istruzione: fread(a,sizeof(int),n,pf). Infatti se notate passiamo n come terzo argomento, cioè diciamo alla funzione di leggere n dati.
Ecco dunque il listato completo sui file con vettori in C:
Proponiamo oggi alcuni esercizi su file e strutture in C. Utilizzeremo i file binari e le funzioni fclose, fopen, fread e fwrite.
Esercizi su file e strutture in C
Il primo esercizio che svolgeremo è una variazione dell’esercizio precedente, proponiamo in aggiunta l’uso delle funzioni, così da approfondirne l’uso.
Creare una struct studente che contiene la matricola, il nome, il cognome e il voto. Creare una funzione/procedura per l’inserimento dei dati e un’altra per la stampa degli studenti inseriti. I dati saranno inseriti e letti in un file binario di nome student.dat.
Innanzitutto creiamo la struct come richiesta nel problema sopra. Dopo creiamo i prototipi delle funzioni che utilizzeremo per inserire e stampare i dati. Entrambe le funzioni prendono come parametro il file pointer, cioè il puntatore al file restituito dalla funzione fopen.
Apriamo il file binariostudent.dat in scrittura e se non ci sono problemi nell’apertura richiamiamo la procedura insert che si occupa di richiedere e registrare i dati in input.
Dopo occorre riaprire il file binario in lettura e verificare che l’apertura non abbia prodotto problemi. Quindi si invoca la procedura print che provvederà a stampare i campi inseriti precedentemente in input.
Notate che la chiusura del file, con fclose, avviene ovviamente sia dopo aver inserito i dati, sia dopo la stampa dei dati. Infatti il file viene aperto due volte, prima in scrittura e poi in lettura.
Creare una struct studente che contiene la matricola, il nome, il cognome e il voto. Il campo matricola dovrà essere generato dal sistema come numero random ad esempio tra 1000 e 1999. Quindi inserire 10 studenti e memorizzarli in una tabella s di tipo studente. Infine copiare i dati in un file binario di nome archivi.txt.
Quindi creaimo un struttura studente e una tabella s che contiene 10 studenti. Generiamo dei numeri casuali con la funzione rand() in modo tale che non ci siano ripetizioni. Per evitare ripetizioni inserisco un altro ciclo for interno che scandisce con un indice j il vettore dall’inizio fino alla posizione in cui si è arrivati con l’indice i. Ovviamente ci possono essere altri metodi per evitare ripetizioni, provate a pensare una soluzione alternativa e inseritela pure nei commmenti sotto.
Stampiamo, solo per controllare i dati inseriti, il nostro vettore e poi ne inseriamo il contenuto nel file archivi.txt utilizzando la funzione fwrite.
Chiaramente prima il file deve essere aperto con la funzione fopen.
#include<stdio.h>
#include <stdlib.h>
#define N 10
typedef struct {
char nome[20];
char cognome[20];
int voto;
int id;
}studente;
studente s[N];
int main(){
int i,j;
FILE *fp;
for(i=0;i<N;i++) {
s[i].id=rand()%1000+1000;
for(j=0;j<i;j++) {
if(s[i].id==s[j].id){
i--;
break;
}
}
printf("\nInserisci il nome:");
scanf("%s",s[i].nome);
printf("Inserisci il cognome:");
scanf("%s",s[i].cognome);
printf("Inserisci il voto:");
scanf("%d",&s[i].voto);
}
for(i=0;i<N;i++) {
printf("%d %s %s %d\n",s[i].id, s[i].nome, s[i].cognome,s[i].voto);
}
if((fp=fopen("archivi.txt", "wb"))==NULL) {
printf("Errore nell'apertura del file");
exit(1);
}
fwrite(&s,sizeof(studente),N,fp);
fclose(fp);
return 0;
}
Realizziamo adesso un altro programma che legge un file binario archivi.txt come quello appena creato.
Realizziamo alcuni esempi sui file di record, al fine di consolidare le funzioni sui file studiate finora. Faremo l’accesso in maniera sequenziale, mentre più avanti studieremo l’accesso diretto. Ricordiamo inoltre che sui file di record le operazioni vengono effettuate un record alla volta.
In modo particolare approfondiremo oggi le funzioni fread e fwrite per la lettura e scrittura sui file binari. Impareremo anche a scrivere il contenuto di un vettore di record su un file binario.
Esempi su file di record
Creare una struct studente che contiene il nome, il cognome e il voto. Dopo inserire 5 studenti e memorizzarli in una tabella s di tipo studente. Infine copiare i dati in un file binario di nome archivi.txt.
Quindi definiamo una struttura studente come descritta nel problema e creiamo la tabella s di tipo studente che conterrà 5 elementi.
Dopo con un semplice ciclo for inizializziamo la tabella s con i dati inseriti dall’utente. Visualizziamo anche il contenuto del record, semplicemente come prova dell’inserimento, non sarebbe infatti necessario. Poi apriamo il file binario archivi.txt in scrittura e controlliamo eventuali messaggi di errore.
Infine copiamo il contenuto del record nel file binario utilizzando la funzione fwrite.
Ecco dunque il listato completo:
#include<stdio.h>
#include <stdlib.h>
#define N 2
typedef struct {
char nome[20];
char cognome[20];
int voto;
} studente;
studente s[N];
main(){
int i;
FILE *file;
for(i=0;i<N;i++) {
printf("\nInserisci il nome:");
scanf("%s",s[i].nome);
printf("Inserisci il cognome:");
scanf("%s",s[i].cognome);
printf("Inserisci il voto:");
scanf("%d",&s[i].voto);
}
/*visualizzo il contenuto del record*/
for(i=0;i<N;i++) {
printf("%s %s %d\n",s[i].nome, s[i].cognome,s[i].voto );
}
if((file=fopen("archivi.txt", "wb"))==NULL) {
printf("Errore nell'apertura del file");
exit(1);
}
fwrite(&s,sizeof(studente),N,file);
fclose(file);
}
Adesso creiamo un altro programma per leggere il file binario appena creato, chiaramente si poteva integrare anche nell’esempio sopra, inserendo solo l’operazione di lettura.