Come muoversi in un file binario

Le operazioni di lettura e scrittura hanno come effetto quello di spostare la posizione corrente nel file. È però anche possibile spostare la posizione corrente in modo arbitrario, senza leggere e scrivere. La funzione che si usa si chiama fseek.

Vediamo ora come si può portare avanti e indietro la posizione corrente. Per avanzare di n byte rispetto alla posizione corrente, si fa:

fseek(fd, n, SEEK_CUR);

Per tornare indietro di n byte, ossia spostare la posizione corrente di n byte indietro, si fa:

fseek(fd, -n, SEEK_CUR);

In altre parole, il secondo parametro indica la nuova posizione relativamente a quella vecchia.

Modifichiamo il programma della pagina precedente dispari.c, in modo tale che legga, uno per volta, tutti gli interi che stanno scritti su file, e al loro posto ci scriva il valore doppio. Questo si può ottenere in questo modo: una volta letto il numero, sappiamo che la posizione corrente è andata sizeof(int) posizioni avanti; per poter scrivere sopra al numero letto occorre quindi tornare indietro di sizeof(int) posizioni. Questo si realizza con l'istruzione

    fseek(fd, -sizeof(int), SEEK_CUR);

Quello che segue è il programma modificato raddoppia.c

/*
  Raddoppia tutti i numeri di un file.
*/

#include<stdlib.h>
#include<stdio.h>

int main() {
  FILE *fd;
  int x, y;
  int res;


			/* apre il file */
  fd=fopen("test.dat", "r+");
  if( fd==NULL ) {
    perror("Errore in apertura del file");
    exit(1);
  }


			/* ciclo di lettura */
  while(1) {

			/* legge un intero */
    res=fread(&x, sizeof(int), 1, fd);
    if( res!=1 )
      break;

			/* riposiziona */
    fseek(fd, -sizeof(int), SEEK_CUR);

			/* mette il doppio in y e lo scrive */
    y=2*x;
    fwrite(&y, sizeof(int), 1, fd);
  }


			/* chiude il file */
  fclose(fd);

  return 0;
}

Vediamo ora come funziona questo programma sul file di esempio. Quando il file viene aperto, la posizione corrente si trova sull'inizio del file:

Si va ora a leggere il primo intero, che è 12. Questo valore viene memorizzato nella variabile x. L'istruzione di lettura ha anche l'effetto secondario di spostare in avanti la posizione corrente all'interno del file:

A questo punto, abbiamo l'istruzione fseek(fd, -sizeof(int), SEEK_CUR);, che sposta la posizione nel file indietro del numero di byte usati per rappresentare un intero. Dal momento che abbiamo prima letto un intero, e poi spostato indietro di un numero uguale di posizioni, quello che si ottiene alla fine è che la posizione nel file coincide con quella iniziale:

Si assegna ora ad y il doppio del valore di x, e si scrive y sul file. Si noti che il numero viene scritto a partire dalla posizione corrente. Quindi, viene scritto 24 (il doppio di 12) a partire dalla posizione iniziale del file. L'operazione di scrittura ha anche l'effetto di spostare in avanti la posizione corrente nel file:

L'effetto complessivo di leggere-spostare indietro-scrivere è che il numero letto viene sostituito con un altro (in questo caso, il doppio). Eseguendo un'altra iterazione del ciclo, si fa la lettura del secondo intero su file, si ritorna indietro, e si scrive il doppio nelle stesse posizioni in cui prima si trovava il secondo numero. Lo stesso vale anche per il terzo, ecc. Quindi, il programma effettivamente sostituisce a ogni numero il suo valore raddoppiato.

Nota: L'istruzione di spostamento della posizione corrente funziona anche sui file di testo. Su questi file è però molto meno utile, dal momento che lo spazio occupato dai dati non è costante. Se si volesse realizzare un programma analogo a quello di sopra, ma che operi su un file di testo, si dovrebbe tenere conto che, se si legge per esempio il numero 8, allora il doppio è 16, che occupa due byte (mentre il precedente ne occupa uno solo).