Je viens de trouver un commentaire dans cette réponse disant que l'utilisation iostream::eof
dans une condition de boucle est "presque certainement fausse". J'utilise généralement quelque chose comme while(cin>>n)
- qui, je suppose, vérifie implicitement EOF.
Pourquoi la vérification de eof utilise- while (!cin.eof())
t-elle explicitement mal?
En quoi est-ce différent de l'utilisation scanf("...",...)!=EOF
en C (que j'utilise souvent sans problème)?
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.EOF
si la fin du fichier est rencontrée avant la première conversion de champ (réussie ou non). Si la fin du fichier est atteinte entre les champs, il retournera le nombre de champs convertis et stockés avec succès. Ce qui rend la comparaisonEOF
erronée..eof()
après la sortie de la boucle.while(fail)
boucle se termine avec à la fois une défaillance réelle et un eof. Réfléchissez si vous avez besoin de 3 ints par itération (disons que vous lisez un point xyz ou quelque chose), mais qu'il n'y a, à tort, que deux ints dans le flux.Réponses:
Parce
iostream::eof
que ne reviendratrue
qu'après avoir lu la fin du flux. Cela n'indique pas que la prochaine lecture sera la fin du flux.Considérez ceci (et supposez que la prochaine lecture se fera à la fin du flux):
Contre cela:
Et sur votre deuxième question: Parce que
est le même que
et pas la même chose que
la source
int
s oustd::string
s ou similaire, le bit EOF est défini lorsque vous extrayez celui juste avant la fin et que l'extraction atteint la fin. Vous n'avez pas besoin de relire. La raison pour laquelle il n'est pas défini lors de la lecture à partir de fichiers est qu'il y a un supplément\n
à la fin. J'ai couvert cela dans une autre réponse . La lecture dechar
s est une question différente car elle n'en extrait qu'un à la fois et ne continue pas à la fin.while (!eof())
cela ne "fonctionnera" pas surint
s oustd::string
s lorsque l'entrée est totalement vide, donc même en sachant qu'il n'y a pas de\n
soin de fin est nécessaire."Hello"
(aucun espace de fin ou\n
) et que astd::string
est extrait, il extraira les lettres deH
ào
, arrêtera l'extraction et alors ne définissez pas le bit EOF. En fait, cela réglerait le bit EOF car c'est l'EOF qui a arrêté l'extraction. Espérant simplement clarifier cela pour les gens.Bottom-line top: Avec une gestion correcte de l'espace blanc, voici comment
eof
peut être utilisé (et même, être plus fiable quefail()
pour la vérification des erreurs):( 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érificationeof()
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:
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 ensemblefailbit
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:
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:
std::ws
ignore tout espace de fuite potentiel (zéro ou plus) dans le flux lors de la définition deeofbit
, 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: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).la source
eofbit
etfailbit
sont fixés, dans l'autrefailbit
n'est défini. Vous n'avez besoin de tester qu'une seule fois après la fin de la boucle, pas à chaque itération; il ne quittera la boucle qu'une seule fois, il vous suffit donc de vérifier pourquoi il a quitté la boucle une fois.while (in >> data)
fonctionne bien pour tous les flux vides.!eof & fail
une boucle passée. Il y a des cas où l'on ne peut pas se fier à cela. Voir le commentaire ci-dessus ( goo.gl/9mXYX ). Quoi qu'il en soit, je ne propose paseof
-check comme l' alternative toujours meilleure . Je dis simplement, il est un moyen possible et (dans certains cas , plus approprié) de le faire, plutôt que « certainement tort! » car il a tendance à être revendiqué ici dans SO.stream >> my_int
où le flux contient par exemple "-":eofbit
etfailbit
sont ensemble. C'est pire que leoperator>>
scénario, où la surcharge fournie par l'utilisateur a au moins la possibilité de s'effacereofbit
avant de revenir pour aider à prendre en charge l'while (s >> x)
utilisation. Plus généralement, cette réponse pourrait utiliser un nettoyage - seule la finalewhile( !(in>>ws).eof() )
est généralement robuste, et elle est enterrée à la fin.Parce que si les programmeurs n'écrivent pas
while(stream >> n)
, ils écrivent éventuellement ceci:Ici, le problème est que vous ne pouvez pas vous
some work on n
passer de vérifier d'abord si la lecture du flux a réussi, car si elle échouait,some work on n
cela produirait un résultat indésirable.Le tout est que
eofbit
,badbit
oufailbit
sont mis après une tentative de lecture du flux. Donc, sistream >> n
échoue, alorseofbit
,badbit
oufailbit
est défini immédiatement, donc c'est plus idiomatique si vous écrivezwhile (stream >> n)
, car l'objet retourné sestream
convertit enfalse
cas d'échec de lecture dans le flux et, par conséquent, la boucle s'arrête. Et il se transforme entrue
si la lecture a réussi et la boucle continue.la source
n
, le programme peut également tomber dans une boucle infinie , si l'opération de flux en échec ne consomme aucune entrée.Les autres réponses ont expliqué pourquoi la logique est erronée
while (!stream.eof())
et comment y remédier. Je veux me concentrer sur quelque chose de différent:De manière générale, la vérification de
eof
uniquement est incorrecte car l'extraction de flux (>>
) peut échouer sans atteindre la fin du fichier. Si vous avez par exempleint n; cin >> n;
et que le flux contienthello
, alors ceh
n'est pas un chiffre valide, donc l'extraction échouera sans atteindre la fin de l'entrée.Ce problème, combiné à l'erreur logique générale de vérification de l'état du flux avant d' essayer de le lire, ce qui signifie que pour N éléments d'entrée, la boucle s'exécutera N + 1 fois, conduit aux symptômes suivants:
Si le flux est vide, la boucle s'exécutera une fois.
>>
échouera (il n'y a pas d'entrée à lire) et toutes les variables qui devaient être définies (parstream >> x
) ne sont en fait pas initialisées. Cela entraîne le traitement des données inutiles, qui peuvent se manifester par des résultats absurdes (souvent des nombres énormes).(Si votre bibliothèque standard est conforme à C ++ 11, les choses sont un peu différentes maintenant: un échec
>>
définit désormais les variables numériques au0
lieu de les laisser non initialisées (sauf pourchar
s).)Si le flux n'est pas vide, la boucle sera exécutée à nouveau après la dernière entrée valide. Étant donné que dans la dernière itération, toutes les
>>
opérations échouent, les variables sont susceptibles de conserver leur valeur par rapport à l'itération précédente. Cela peut se manifester par «la dernière ligne est imprimée deux fois» ou «le dernier enregistrement d'entrée est traité deux fois».(Cela devrait se manifester un peu différemment depuis C ++ 11 (voir ci-dessus): vous obtenez maintenant un "enregistrement fantôme" de zéros au lieu d'une dernière ligne répétée.)
Si le flux contient des données mal formées mais que vous ne faites que vérifier
.eof
, vous vous retrouvez avec une boucle infinie.>>
ne parviendra pas à extraire les données du flux, de sorte que la boucle tourne en place sans jamais atteindre la fin.Pour récapituler: La solution est de tester le succès de l'
>>
opération elle - même, de ne pas utiliser une distincte.eof()
méthode:while (stream >> n >> m) { ... }
, comme en C tester le succès de l'scanf
appel lui - même:while (scanf("%d%d", &n, &m) == 2) { ... }
.la source