Bottom-line top: Avec une gestion correcte de l'espace blanc, voici comment eof
peut être utilisé (et même, être plus fiable que fail()
pour la vérification des erreurs):
while( !(in>>std::ws).eof() ) {
int data;
in >> data;
if ( in.fail() ) /* handle with break or throw */;
// now use data
}
( Merci Tony D pour la suggestion de mettre en évidence la réponse. Voir son commentaire ci-dessous pour un exemple de pourquoi il est plus robuste. )
Le principal argument contre l'utilisation eof()
semble manquer d'une subtilité importante sur le rôle de l'espace blanc. Ma proposition est que, la vérification eof()
explicite n'est pas seulement " toujours erronée " - ce qui semble être une opinion prépondérante dans ce filetage SO et similaire -, mais avec une gestion appropriée de l'espace blanc, elle fournit un filtre plus propre et plus fiable. gestion des erreurs, et est la solution toujours correcte (bien que, pas nécessairement le plus tersest).
Pour résumer ce qui est suggéré comme la terminaison «correcte» et l'ordre de lecture est le suivant:
int data;
while(in >> data) { /* ... */ }
// which is equivalent to
while( !(in >> data).fail() ) { /* ... */ }
L'échec dû à une tentative de lecture au-delà de eof est considéré comme la condition de résiliation. Cela signifie qu'il n'y a pas de moyen simple de distinguer un flux réussi d'un flux qui échoue vraiment pour des raisons autres que eof. Prenez les flux suivants:
1 2 3 4 5<eof>
1 2 a 3 4 5<eof>
a<eof>
while(in>>data)
se termine par un ensemble failbit
pour les trois entrées. Dans le premier et le troisième, eofbit
est également défini. Donc, au-delà de la boucle, il faut une logique supplémentaire très moche pour distinguer une entrée correcte (1ère) des entrées incorrectes (2e et 3e).
Attendez-vous à ce qui suit:
while( !in.eof() )
{
int data;
in >> data;
if ( in.fail() ) /* handle with break or throw */;
// now use data
}
Ici, in.fail()
vérifie que tant qu'il y a quelque chose à lire, c'est le bon. Son but n'est pas un simple terminateur de boucle while.
Jusqu'ici tout va bien, mais que se passe-t-il s'il y a un espace de fuite dans le flux - ce qui semble être la principale préoccupation contre en eof()
tant que terminateur?
Nous n'avons pas besoin de renoncer à notre gestion des erreurs; il suffit de manger l'espace blanc:
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
ignore tout espace de fuite potentiel (zéro ou plus) dans le flux lors de la définition de eofbit
, et non defailbit
. Donc, in.fail()
fonctionne comme prévu, tant qu'il y a au moins une donnée à lire. Si des flux entièrement vierges sont également acceptables, la forme correcte est:
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
}
Résumé: Une construction correcte while(!eof)
est non seulement possible et non fausse, mais elle permet de localiser les données dans la portée et fournit une séparation plus nette entre la vérification des erreurs et le fonctionnement normal. Cela étant dit, while(!fail)
est incontestablement un idiome plus courant et plus laconique, et peut être préféré dans des scénarios simples (données uniques par type de lecture).
scanf(...) != EOF
ne fonctionnera pas non plus en C, carscanf
renvoie le nombre de champs correctement analysés et attribués. La condition correcte estscanf(...) < n
oùn
est le nombre de champs dans la chaîne de format.