In conclusione: con una corretta gestione degli spazi bianchi, è eof
possibile utilizzare quanto segue (e persino, essere più affidabile rispetto fail()
al controllo degli errori):
while( !(in>>std::ws).eof() ) {
int data;
in >> data;
if ( in.fail() ) /* handle with break or throw */;
// now use data
}
( Grazie a Tony D per il suggerimento di evidenziare la risposta. Vedi il suo commento qui sotto per un esempio del perché questo è più robusto. )
L'argomento principale contro l'uso eof()
sembra mancare di una sottigliezza importante sul ruolo dello spazio bianco. La mia proposta è che, il controllo eof()
esplicito non solo non sia " sempre sbagliato " - che sembra essere un'opinione prevalente in questo e simili thread SO -, ma con una corretta gestione degli spazi bianchi, fornisce un sistema più pulito e affidabile gestione degli errori, ed è la soluzione sempre corretta (anche se non necessariamente la più testata).
Riassumendo ciò che viene suggerito come terminazione "corretta" e ordine di lettura è il seguente:
int data;
while(in >> data) { /* ... */ }
// which is equivalent to
while( !(in >> data).fail() ) { /* ... */ }
L'errore dovuto al tentativo di lettura oltre eof viene considerato come condizione di terminazione. Ciò significa che non esiste un modo semplice per distinguere tra un flusso di successo e uno che non riesce realmente per ragioni diverse da eof. Prendi i seguenti flussi:
1 2 3 4 5<eof>
1 2 a 3 4 5<eof>
a<eof>
while(in>>data)
termina con un set failbit
per tutti e tre gli input. Nel primo e terzo, eofbit
è anche impostato. Quindi, oltre il loop, è necessaria una logica extra molto brutta per distinguere un input corretto (1 °) da quelli impropri (2 ° e 3 °).
Considerando che, prendere quanto segue:
while( !in.eof() )
{
int data;
in >> data;
if ( in.fail() ) /* handle with break or throw */;
// now use data
}
Qui, in.fail()
verifica che fino a quando c'è qualcosa da leggere, è quello corretto. Il suo scopo non è un semplice terminatore di ciclo continuo.
Fin qui tutto bene, ma cosa succede se c'è spazio finale nel flusso - quale potrebbe sembrare la principale preoccupazione contro eof()
come terminatore?
Non abbiamo bisogno di rinunciare alla nostra gestione degli errori; mangia lo spazio bianco:
while( !in.eof() )
{
int data;
in >> data >> ws; // eat whitespace with std::ws
if ( in.fail() ) /* handle with break or throw */;
// now use data
}
std::ws
salta qualsiasi potenziale (zero o più) spazio finale nello stream durante l'impostazione di eofbit
, e non difailbit
. Quindi, in.fail()
funziona come previsto, purché ci sia almeno un dato da leggere. Se anche i flussi in bianco sono accettabili, la forma corretta è:
while( !(in>>ws).eof() )
{
int data;
in >> data;
if ( in.fail() ) /* handle with break or throw */;
/* this will never fire if the eof is reached cleanly */
// now use data
}
Riepilogo: una costruzione corretta while(!eof)
non è solo possibile e non sbagliata, ma consente di localizzare i dati nell'ambito e fornisce una separazione più chiara del controllo degli errori dall'azienda come al solito. Detto questo, while(!fail)
è indiscutibilmente un linguaggio più comune e conciso, e può essere preferito in scenari semplici (dati singoli per tipo di lettura).
scanf(...) != EOF
non funzionerà neanche in C, perchéscanf
restituisce il numero di campi analizzati e assegnati correttamente. La condizione corretta èscanf(...) < n
dove sin
trova il numero di campi nella stringa di formato.