Pourquoi est-iostream::eof à l'intérieur d'une condition de boucle considéré comme mauvais?

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
446
répondu Xeo 2016-03-15 13:57:58

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).

88
répondu sly 2016-08-13 19:54:54

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.

59
répondu Nawaz 2016-10-20 17:58:42
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.

-3
répondu Mohammed Younus 2017-03-10 19:30:06