Perché iostream::eof dentro una condizione di loop è considerato sbagliato in C++?

Non è necessariamente sbagliato. È sbagliato solo qualcosa come il 99,99% delle volte (o giù di lì).

Ci sono un paio di problemi con esso. Let’s start by considering a distillation of typical code that attempts to use it:

  1. while (!somefile.eof()) { 
  2. file.read(data); 
  3. process(data); 

Il primo problema qui è anche il più semplice da evitare: controllare solo la fine del file è generalmente inadeguato. Se tentate di leggere, e fallisce senza che voi raggiungiate la fine del file, questo entrerà in un ciclo infinito, cercando di leggere dal file, e fallendo ripetutamente - ma non facendo nulla per risolvere il problema o uscire dal ciclo.

Fixing that is fairy easy though-instead of checking for eof(), you might instead try something like: while (somefile.good()) { ... }. Questo è almeno un po' un miglioramento: almeno ora, se la lettura dal file fallisce, non si entra in un ciclo infinito.

Ha ancora un altro problema abbastanza serio. Questo problema riguarda il sequenziamento. Inizia controllando se la lettura dal file è fallita. Poi legge alcuni dati, elabora i dati e ripete.

Il problema è semplice: legge i dati, poi elabora i dati, e solo dopo averli elaborati, cerca di controllare se effettivamente ha letto i dati correttamente. Nel caso non fosse chiaro, lasciatemi ripetere (perché è importante): cerca di leggere alcuni dati, poi, che ci sia riuscito o meno, cerca di elaborare come se ci fosse riuscito, e solo dopo aver finito di elaborare dati che potrebbe non aver effettivamente letto, controlla se ha letto quei dati correttamente.

Spero che questo renda ovvio l'approccio corretto: dobbiamo cambiare la sequenza: dobbiamo provare a leggere alcuni dati, poi se e solo se la lettura dei dati è riuscita, possiamo elaborare quei dati. And continue doing that until reading data fails. When it does fail, then we an sort out whether we reached the end of the file (so we read all the data correctly, and life is good) or it failed for some other reason (so we may have a problem).

To do that, we typically want to change the loop to check the result of the read itself. For example:

  1. while (std::getline(somefile, somestring)) 
  2. process(somestring); 

or:

  1. while (somefile.read(buffer, buffer_size)) 
  2. process(buffer, somefile.gcount()); 

Then after that loop we can check why we exited the loop:

  1. if (somefile.eof()) 
  2. std::cout << “processed all the datan”; 
  3. else if (somefile.fail()) 
  4. std::cerr << “Error reading data from filen”; 
  5. else if (somefile.bad()) 
  6. std::cerr << “Serious, unrecoverable error attempting to read filen”; 

Of these, fail() usually means a conversion has failed—for example, we were trying to read a number, but what we found in the file was “yes”. Al contrario, bad() tipicamente significa che abbiamo ottenuto un errore dal sistema operativo che indica che c'è stato un problema nella lettura del file, come ad esempio: "abbiamo perso la connessione al server dove si trovava quel file", o forse: "c'è stato un errore CRC nel tentativo di leggere quel settore, quindi alcuni dei dati devono essere cattivi", o qualcosa del genere.