Ich habe gerade einen Kommentar in dieser Antwort gefunden, der besagt, dass die Verwendung iostream::eof
in einer Schleifenbedingung "mit ziemlicher Sicherheit falsch" ist. Ich benutze im Allgemeinen so etwas wie while(cin>>n)
- was ich implizit auf EOF prüfe.
Warum wird die Überprüfung auf eof explizit while (!cin.eof())
falsch verwendet?
Wie unterscheidet es sich von der Verwendung scanf("...",...)!=EOF
in C (die ich oft ohne Probleme verwende)?
scanf(...) != EOF
funktioniert auch in C nicht, dascanf
die Anzahl der erfolgreich analysierten und zugewiesenen Felder zurückgegeben wird. Die richtige Bedingung ist,scanf(...) < n
won
die Anzahl der Felder in der Formatzeichenfolge ist.EOF
wenn das Dateiende vor der ersten Feldkonvertierung festgestellt wurde (erfolgreich oder nicht). Wenn das Dateiende zwischen den Feldern erreicht ist, wird die Anzahl der Felder zurückgegeben, die erfolgreich konvertiert und gespeichert wurden. Was den VergleichEOF
falsch macht..eof()
nach dem Beenden der Schleife.while(fail)
ausdenken, in dem die Schleife sowohl mit einem tatsächlichen Fehler als auch mit einem Eof endet. Überlegen Sie, ob Sie 3 Zoll pro Iteration benötigen (sagen wir, Sie lesen einen xyz-Punkt oder etwas anderes), aber fälschlicherweise sind nur zwei Zoll im Stream.Antworten:
Denn
iostream::eof
wird ersttrue
nach dem Lesen des Stream-Endes zurückkehren. Es zeigt nicht an, dass der nächste Lesevorgang das Ende des Streams sein wird.Bedenken Sie dies (und nehmen Sie an, dass der nächste Lesevorgang am Ende des Streams erfolgt):
Gegen das:
Und zu Ihrer zweiten Frage: Weil
ist das gleiche wie
und nicht das gleiche wie
quelle
int
s oderstd::string
s oder ähnlichem, der EOF - Bit wird gesetzt , wenn Sie die ein kurz vor dem Ende zu extrahieren und die Extraktion trifft das Ende. Sie müssen nicht erneut lesen. Der Grund, warum es beim Lesen aus Dateien nicht festgelegt wird, ist, dass es\n
am Ende ein Extra gibt . Ich habe dies in einer anderen Antwort behandelt . Das Lesen vonchar
s ist eine andere Sache, da es jeweils nur einen extrahiert und nicht weiter das Ende erreicht.while (!eof())
sie beiint
s oderstd::string
s nicht "funktioniert", wenn die Eingabe vollständig leer ist. Selbst wenn Sie wissen, dass keine nachlaufende\n
Sorgfalt erforderlich ist."Hello"
(kein nachfolgendes Leerzeichen oder\n
) enthält und astd::string
extrahiert wird, werden die Buchstaben vonH
bis extrahiert, das Extrahiereno
wird gestoppt und dann nicht das EOF-Bit setzen. Tatsächlich würde es das EOF-Bit setzen, weil es die EOF war, die die Extraktion stoppte. Ich hoffe nur, das für die Leute zu klären.Fazit oben: Bei richtiger Behandlung von Leerzeichen kann Folgendes
eof
verwendet werden (und sogar zuverlässiger sein alsfail()
bei der Fehlerprüfung):( Vielen Dank an Tony D für den Vorschlag, die Antwort hervorzuheben. In seinem Kommentar unten finden Sie ein Beispiel dafür, warum dies robuster ist. )
Dem Hauptargument gegen die Verwendung
eof()
scheint eine wichtige Subtilität über die Rolle des Leerraums zu fehlen. Mein Vorschlag ist, dasseof()
explizites Prüfen nicht nur nicht " immer falsch " ist - was in diesem und ähnlichen SO-Threads eine übergeordnete Meinung zu sein scheint -, sondern bei ordnungsgemäßem Umgang mit Leerraum auch für einen saubereren und zuverlässigeren Umgang sorgt Fehlerbehandlung und ist die immer richtige Lösung (obwohl nicht unbedingt die engste).Um zusammenzufassen, was als "richtige" Kündigungs- und Lesereihenfolge vorgeschlagen wird, ist Folgendes:
Der Fehler aufgrund eines Leseversuchs über eof hinaus wird als Beendigungsbedingung angesehen. Dies bedeutet, dass es keine einfache Möglichkeit gibt, zwischen einem erfolgreichen Stream und einem Stream zu unterscheiden, der aus anderen Gründen als eof wirklich fehlschlägt. Nehmen Sie die folgenden Streams:
1 2 3 4 5<eof>
1 2 a 3 4 5<eof>
a<eof>
while(in>>data)
endet mit einem Satzfailbit
für alle drei Eingänge. Im ersten und dritteneofbit
wird auch gesetzt. Nach der Schleife braucht man also eine sehr hässliche zusätzliche Logik, um einen richtigen Eingang (1.) von einem falschen (2. und 3.) zu unterscheiden.Nehmen Sie Folgendes:
Hier wird
in.fail()
überprüft, ob etwas zu lesen ist, es ist das richtige. Sein Zweck ist nicht nur ein While-Loop-Terminator.So weit so gut, aber was passiert, wenn im Stream nachlaufender Speicherplatz vorhanden ist - was klingt nach der größten Sorge
eof()
als Terminator?Wir müssen unsere Fehlerbehandlung nicht aufgeben. iss einfach den Leerraum auf:
std::ws
Überspringt jeglichen potenziellen (null oder mehr) nachgestellten Speicherplatz im Stream, während daseofbit
und nicht das festgelegt wirdfailbit
. Funktioniert alsoin.fail()
wie erwartet, solange mindestens eine Daten gelesen werden muss. Wenn auch leere Streams akzeptabel sind, lautet die richtige Form:Zusammenfassung: Eine ordnungsgemäß erstellte Konstruktion
while(!eof)
ist nicht nur möglich und nicht falsch, sondern ermöglicht auch die Lokalisierung von Daten innerhalb des Gültigkeitsbereichs und bietet eine sauberere Trennung von Fehlerprüfung und Business as usual. Davon abgesehen,while(!fail)
ist inarguably ein häufige und terse Idiom und kann in einfachen (Einzeldaten pro Lesetyp) Szenarien bevorzugt sein.quelle
eofbit
und gesetztfailbit
sind, in dem anderen nurfailbit
gesetzt. Sie müssen dies nur einmal testen, nachdem die Schleife beendet wurde, nicht bei jeder Iteration. Die Schleife wird nur einmal verlassen, sodass Sie nur einmal überprüfen müssen, warum sie die Schleife verlassen hat.while (in >> data)
funktioniert gut für alle leeren Streams.!eof & fail
vergangene Schleife identifiziert werden kann . Es gibt Fälle, in denen man sich nicht darauf verlassen kann. Siehe obigen Kommentar ( goo.gl/9mXYX ). Auf jeden Fall schlage icheof
-check nicht als die immer bessere Alternative vor. Ich sage nur, es ist eine mögliche und (in einigen Fällen angemessenere) Art und Weise, dies zu tun, anstatt "mit Sicherheit falsch!" wie es hier in SO tendenziell behauptet wird.stream >> my_int
wo der Strom enthält zB „-“:eofbit
undfailbit
sind einstellen. Dies ist schlimmer als in demoperator>>
Szenario, in dem die vom Benutzer bereitgestellte Überlastung zumindest die Option hat,eofbit
vor der Rückkehr zu löschen, um diewhile (s >> x)
Nutzung zu unterstützen . Im Allgemeinen könnte diese Antwort eine Bereinigung gebrauchen - nur das Finalewhile( !(in>>ws).eof() )
ist im Allgemeinen robust und am Ende begraben.Denn wenn Programmierer nicht schreiben
while(stream >> n)
, schreiben sie möglicherweise Folgendes:Hier besteht das Problem darin, dass Sie nicht darauf verzichten müssen,
some work on n
zuerst zu überprüfen, ob das Lesen des Streams erfolgreich war. Wenn dies nicht erfolgreich war,some work on n
würde dies zu unerwünschten Ergebnissen führen.Der springende Punkt ist , dass,
eofbit
,badbit
, oderfailbit
eingestellt werden , nachdem ein Versuch gemacht wird , aus dem Stream zu lesen. Also , wennstream >> n
nicht, danneofbit
,badbit
oderfailbit
sofort gesetzt wird, so dass ihre mehr idiomatischen , wenn Sie schreibenwhile (stream >> n)
, weil das zurückgegebene Objektstream
Konvertiten ,false
wenn irgendein Fehler war aus dem Strom in dem Lese und damit die Schleife beendet. Und es wird konvertiert,true
wenn der Lesevorgang erfolgreich war und die Schleife fortgesetzt wird.quelle
n
kann das Programm auch in eine Endlosschleife fallen , wenn die fehlgeschlagene Stream-Operation keine Eingabe verbraucht.Die anderen Antworten haben erklärt, warum die Logik falsch ist
while (!stream.eof())
und wie man sie behebt. Ich möchte mich auf etwas anderes konzentrieren:Im Allgemeinen ist die Überprüfung
eof
nur auf falsch, da die Stream-Extraktion (>>
) fehlschlagen kann, ohne das Ende der Datei zu erreichen. Wenn Sie zB habenint n; cin >> n;
und der Stream enthälthello
,h
ist dies keine gültige Ziffer, sodass die Extraktion fehlschlägt, ohne das Ende der Eingabe zu erreichen.Dieses Problem führt zusammen mit dem allgemeinen logischen Fehler, den Stream-Status vor dem Versuch, daraus zu lesen, zu überprüfen , was bedeutet, dass die Schleife für N Eingabeelemente N + 1 Mal ausgeführt wird, zu den folgenden Symptomen:
Wenn der Stream leer ist, wird die Schleife einmal ausgeführt.
>>
wird fehlschlagen (es gibt keine zu lesende Eingabe) und alle Variablen, die gesetzt werden sollten (vonstream >> x
), sind tatsächlich nicht initialisiert. Dies führt dazu, dass Mülldaten verarbeitet werden, die sich als unsinnige Ergebnisse (häufig große Zahlen) manifestieren können.(Wenn Ihre Standardbibliothek C ++ 11 entspricht, sind die Dinge jetzt etwas anders: Ein Fehler
>>
setzt jetzt numerische Variablen auf,0
anstatt sie nicht initialisiert zu lassen (mit Ausnahme vonchar
s).)Wenn der Stream nicht leer ist, wird die Schleife nach der letzten gültigen Eingabe erneut ausgeführt. Da in der letzten Iteration alle
>>
Operationen fehlschlagen, behalten Variablen wahrscheinlich ihren Wert aus der vorherigen Iteration bei. Dies kann sich als "die letzte Zeile wird zweimal gedruckt" oder "der letzte Eingabedatensatz wird zweimal verarbeitet" manifestieren.(Dies sollte sich seit C ++ 11 etwas anders manifestieren (siehe oben): Jetzt erhalten Sie einen "Phantom Record" von Nullen anstelle einer wiederholten letzten Zeile.)
Wenn der Stream fehlerhafte Daten enthält, Sie aber nur nachsehen
.eof
, erhalten Sie eine Endlosschleife.>>
Es werden keine Daten aus dem Stream extrahiert, sodass sich die Schleife dreht, ohne jemals das Ende zu erreichen.Um es noch einmal zusammenzufassen: Die Lösung besteht darin, den Erfolg der
>>
Operation selbst zu testen und keine separate.eof()
Methode zu verwenden:while (stream >> n >> m) { ... }
Genau wie in C testen Sie den Erfolg desscanf
Aufrufs selbst :while (scanf("%d%d", &n, &m) == 2) { ... }
.quelle