Pourquoi est-iostream::eof à l'intérieur d'une condition de boucle considéré comme mauvais?
je viens de trouver un commentaire dans cette" réponse 151960920 "disant que l'utilisation de iostream::eof
dans une condition de boucle est"presque certainement faux". J'utilise généralement quelque chose comme while(cin>>n)
- qui je suppose vérifie implicitement pour EOF, pourquoi est-ce que vérifier pour eof explicitement en utilisant iostream::eof
faux?
en quoi est-il différent de l'utilisation de scanf("...",...)!=EOF
en C (que j'utilise souvent sans problème)?
4 réponses
parce que iostream::eof
ne retournera true
qu'après en lisant la fin du ruisseau. Il n' pas indiquent que la prochaine lecture sera la fin du flux.
(et d'assumer alors la prochaine lecture sera à la fin du flux):
while(!inStream.eof()){
int data;
// yay, not end of stream yet, now read ...
inStream >> data;
// oh crap, now we read the end and *only* now the eof bit will be set (as well as the fail bit)
// do stuff with (now uninitialized) data
}
contre ceci:
int data;
while(inStream >> data){
// when we land here, we can be sure that the read was successful.
// if it wasn't, the returned stream from operator>> would be converted to false
// and the loop wouldn't even be entered
// do stuff with correctly initialized data (hopefully)
}
et sur votre deuxième question: parce que
if(scanf("...",...)!=EOF)
est le même que
if(!(inStream >> data).eof())
et pas la même chose que
if(!inStream.eof())
inFile >> data
Bottom-line top: avec une bonne manipulation de l'espace blanc, ce qui suit est la façon dont 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 ceci est plus robuste. )
l'argument principal contre l'utilisation "151970920 semble manquer une importante subtilité sur le rôle de l'espace blanc. Ma proposition est que, vérifier eof()
explicitement n'est pas seulement pas " toujours tort "-qui semble être une opinion dominante dans ce et similaire fils de SO -, mais avec une bonne manipulation de l'espace blanc, il prévoit une manipulation d'erreur plus propre et plus fiable, et est le toujours correct solution (bien que, pas nécessairement le plus tersest).
pour résumer ce qui est suggéré comme la terminaison "appropriée" et lire l'ordre est le suivant:
int data;
while(in >> data) { /* ... */ }
// which is equivalent to
while( !(in >> data).fail() ) { /* ... */ }
La défaillance due à lire tentative au-delà du folklore qui est considéré comme la condition de résiliation. Cela signifie qu'il n'y a pas de moyen facile de faire la distinction entre un flux réussi et un flux qui échoue vraiment pour des raisons autres que l'EF. Prenez les cours d'eau 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 tous trois entrées. Dans le premier et le troisième, eofbit
est également placé. Ainsi, après la boucle, il faut une logique supplémentaire très laide pour distinguer une entrée correcte (1) de celles qui ne le sont pas (2 et 3).
considérant 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 la bonne. Son but n'est pas un simple terminateur de boucle.
jusqu'à présent si bon, mais que se passe-t-il s'il y a de l'espace de fuite dans le flux -- qu'est-ce qui ressemble à la préoccupation majeure contre eof()
comme terminator?
Nous ne devons pas abandonner notre gestion des erreurs; il suffit de manger de la white-space:
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
saute toute potentiel (zéro ou plus) espace de fuite dans le flux tout en réglant le eofbit
, et pas le failbit
. Ainsi, in.fail()
fonctionne comme prévu, tant qu'il y a au moins une des données à lire. Si les flux entièrement vierges sont également acceptables, alors le bon formulaire 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 while(!eof)
bien construite est non seulement possible et pas erronée, mais permet aux données d'être localisées dans le champ d'application, et fournit un nettoyeur de séparation de vérification d'erreur d'affaires comme d'habitude. Cela dit, while(!fail)
est incontestablement un idiome plus courant et plus concis, et peut être préféré dans des scénarios simples (données uniques par type de lecture).
parce que si les programmeurs n'écrivent pas while(stream >> n)
, ils peuvent écrire ceci:
while(!stream.eof())
{
stream >> n;
//some work on n;
}
ici, le problème est, vous ne pouvez pas faire some work on n
sans d'abord vérifier si le stream lire a réussi, parce que si elle a échoué, votre some work on n
produirait un résultat indésirable.
le point entier est que, eofbit
, badbit
, ou failbit
sont mis après une tentative est faite pour lire à partir du flux. donc si stream >> n
échoue, alors eofbit
, badbit
, ou failbit
est placé immédiatement, donc son plus idiomatique si vous écrivez while (stream >> n)
, parce que l'objet retourné stream
convertit en false
s'il y avait une défaillance dans la lecture du flux et par conséquent la boucle s'arrête. Et il convertit en true
si la lecture a été réussie et la boucle continue.
1 while (!read.fail()) {
2 cout << ch;
3 read.get(ch);
4 }
si vous utilisez la ligne 2 en 3 et la ligne 3 en 2, vous obtiendrez ch
imprimé Deux fois.
Donc cout avant de lire.