Pourquoi iostream::eof à l’intérieur d’une condition de boucle est-il considéré comme mauvais en C++ ?


Ce n'est pas nécessairement mauvais. C'est seulement faux quelque chose comme 99,99% du temps (ou à peu près).

Il y a quelques problèmes avec lui. 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); 

Le premier problème ici est aussi le plus simple à éviter : se contenter de vérifier la fin du fichier est généralement inadéquat. Si vous tentez de lire, et que cela échoue sans que vous atteigniez la fin du fichier, cela va entrer dans une boucle infinie, essayant de lire le fichier, et échouant de manière répétée - mais ne faisant rien pour résoudre le problème ou s'échapper de la boucle.

Réparer cela est assez facile cependant - au lieu de vérifier pour eof(), vous pourriez plutôt essayer quelque chose comme : while (somefile.good()) { ... }. C'est au moins un peu une amélioration - au moins maintenant, si la lecture du fichier échoue, vous n'entrerez pas dans une boucle infinie.

Il a encore un autre problème assez grave cependant. Ce problème concerne le séquençage. Il commence par vérifier si la lecture du fichier a échoué. Ensuite, il lit quelques données, traite les données et répète.

Le problème avec cela est simple : il lit les données, puis traite les données, et seulement après avoir traité les données, tente de vérifier s'il a effectivement lu les données correctement. Au cas où ce n'était pas clair, laissez-moi répéter (parce que c'est important) : il essaie de lire certaines données, puis, que cela ait réussi ou non, il essaie de traiter comme s'il avait réussi, et seulement après avoir fini de traiter des données qu'il n'a peut-être pas réellement lues, il vérifie s'il a lu ces données correctement.

J'espère que cela rend évidente l'approche correcte : nous devons changer la séquence : nous devons essayer de lire certaines données, puis si et seulement si la lecture des données a réussi, nous pouvons traiter ces données. 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”. En revanche, bad() signifie généralement que nous avons reçu une erreur du système d'exploitation indiquant qu'il y avait un problème de lecture du fichier, par exemple : "nous avons perdu la connexion avec le serveur où se trouvait ce fichier", ou peut-être : "il y a eu une erreur CRC en essayant de lire ce secteur, donc certaines des données doivent être mauvaises", ou quelque chose de cet ordre.