Pourquoi ifstream.eof () ne retourne-t-il pas VRAI après avoir lu la dernière ligne d'un fichier?

11

Lorsqu'un débutant commence à lire des ifstreams, son instinct est de lire le fichier en utilisant une boucle qui ressemble généralement à ceci:

while (!ifstream.eof()
{
...
}

Cependant, lorsque j'ai utilisé ce code, j'ai remarqué qu'il ne s'arrêtait pas avant d'avoir lu deux fois la dernière ligne du fichier. Les programmeurs C ++ notent que ce n'est pas vraiment la façon de lire un fichier. Au lieu de cela, ils recommandent généralement à quiconque a besoin de lire un fichier d'utiliser une boucle comme celle-ci:

while (ifstream >> someVar)
{
...
}

Pourquoi le premier morceau de code ne fonctionne-t-il pas toujours correctement?

moonman239
la source
J'aurais pensé qu'il y en aurait un double, mais je n'en trouve pas ici. Il existe de nombreux doublons sur stackoverflow.
David Hammen

Réponses:

4

La while (!ifstream.eof())boucle ne fonctionne pas, car les flux / fichiers en C et C ++ ne prédisent pas quand vous avez atteint la fin du fichier, mais indiquent plutôt si vous avez essayé de lire après la fin du fichier.

Si la dernière ligne du fichier se termine par un caractère newline ( \n), la plupart des actions de lecture s'arrêteront de lire lorsqu'elles auront rencontré ce caractère et elles ne détecteront pas qu'il s'agit du dernier caractère du fichier. Lors de la prochaine action de lecture, il se peut même que davantage de caractères aient été ajoutés et que la lecture réussisse à les extraire.

La boucle utilisant l'opérateur d'extraction de flux ( while (ifstream >> someVar)) fonctionne car le résultat de l'opérateur d'extraction de flux est évalué à faux s'il ne peut pas extraire un élément du bon type. Cela se produit également s'il ne reste aucun caractère à lire.

Bart van Ingen Schenau
la source
4

Cependant, les programmeurs C ++ notent que ce qui se produit toujours, c'est que cin.eof () ne retourne "vrai" qu'après que la dernière ligne a été lue deux fois.

Ce n'est pas ce qui se passe. Le eofbitne joue aucun rôle dans la conversion en booléen ( stream::operator bool(ou operator void*en ancien c ++)). Seuls les badbitet failbitsont impliqués.

Supposons que vous lisez un fichier contenant des nombres séparés par des espaces. Une boucle basée autour cin.eof()sera inévitablement soit fausse, soit remplie de iftests. Vous ne lisez pas avant l'EOF. Vous lisez des chiffres. Alors, faites en sorte que votre code exprime cette logique:

while (stream >> some_var) {
    process_value(some_var);
}

Cela fonctionnera que la dernière ligne du fichier se termine par 0 42\nou juste 0 42(pas de nouvelle ligne à la fin de la dernière ligne du fichier). Si le fichier se termine par 0 42\n, la dernière bonne lecture récupérera la valeur 42 et lira ce marqueur de fin de ligne final. Notez que le marqueur EOF n'a pas encore été lu. La fonction process_valueest appelée avec 42. Le prochain appel à l'opérateur d'extraction de flux >> lit l'EOF, et puisque rien n'a été extrait, les deux eofbitet failbitseront définis.

Supposons en revanche que le fichier se termine par 0 42(pas de nouvelle ligne à la fin de la dernière ligne). La dernière bonne lecture récupérera la valeur 42 se terminant sur le marqueur EOF. Vraisemblablement, vous voulez traiter cela 42. C'est pourquoi le eofbitne joue pas de rôle dans l'opérateur de conversion booléen du flux d'entrée. Lors du prochain appel à l'opérateur d'extraction de flux >>, la machine sous-jacente voit rapidement que le eofbitest déjà défini. Cela se traduit rapidement par la définition de la failbit.

Pourquoi le premier morceau de code ne fonctionne-t-il pas toujours correctement?

Parce que vous ne devriez pas rechercher EOF comme condition de boucle. La condition de boucle doit exprimer ce que vous essayez de faire, c'est-à-dire (par exemple), extraire des nombres d'un flux.

David Hammen
la source