Ist die Inkonsistenz von C ++ 's istream :: eof () ein Fehler in der Spezifikation oder ein Fehler in der Implementierung?

8

Das folgende Programm demonstriert eine Inkonsistenz in der Art, wie std :: istream (speziell in meinem Testcode, std :: istringstream) eof () setzt.

%Vor%

So wird eof () "ausgelöst", wenn Sie das Zeichen vor das tatsächliche Dateiende lesen. Wenn der Stream jedoch leer ist, wird er nur ausgelöst, wenn Sie tatsächlich versuchen, ein Zeichen zu lesen. Bedeutet eof () "du hast gerade versucht, das Ende zu lesen?" oder "Wenn Sie versuchen, wieder zu lesen, werden Sie am Ende gehen?" Die Antwort ist inkonsistent.

Darüber hinaus hängt es vom Compiler ab, ob die Assert ausgelöst wird oder nicht. Apple Clang 4.1 zündet beispielsweise die Assertion (hebt eof () beim Lesen des vorhergehenden Zeichens auf). GCC 4.7.2, zum Beispiel, nicht.

Diese Inkonsistenz macht es schwer, sinnvolle Schleifen zu schreiben, die einen Stream lesen, aber sowohl leere als auch nichtleere Ströme gut verarbeiten.

OPTION 1:

%Vor%

OPTION 2:

%Vor%

Also, Freunde, wie schreibe ich eine Schleife, die einen Strom durchsucht, sich mit jedem Zeichen der Reihe nach befasst, jedes Zeichen handhabt, aber ohne Zwischenfälle entweder aufhört, wenn wir das EOF treffen oder wenn der Strom leer ist Beginnt mit, fängt nie an?

Und okay, die tiefere Frage: Ich habe die Intuition, dass peek () diese eof () - Inkonsequenz irgendwie umgehen könnte, aber ... heiliger Mist! Warum die Inkonsistenz?

    
OldPeculier 02.11.2012, 23:20
quelle

5 Antworten

9

Das eof() -Flag ist nur nützlich, um festzustellen, ob Sie nach einer Operation das Ende der Datei erreicht haben. Die primäre Verwendung besteht darin, eine Fehlermeldung zu vermeiden, wenn das Lesen einigermaßen fehlgeschlagen ist, weil nichts mehr zu lesen war. Der Versuch, eine Schleife oder etwas unter Verwendung von eof() zu steuern, muss fehlschlagen. In allen Fällen müssen Sie nach überprüfen, wenn Sie versucht haben zu lesen, ob das Lesen erfolgreich war. Vor dem Versuch kann der Stream nicht wissen, was Sie lesen werden.

Die Semantik von eof() wird genau definiert, wenn "dieses Flag beim Lesen des Streams gesetzt wird und der Streampuffer einen Fehler zurückgibt". Es ist nicht ganz so einfach, diese Aussage zu finden, wenn ich mich richtig erinnere, aber das ist es, was kommt. Irgendwann sagt der Standard auch, dass der Stream in einer Situation mehr lesen darf, als es sein muss, was dazu führen kann, dass eof() gesetzt wird, wenn Sie es nicht unbedingt erwarten. Ein solches Beispiel ist das Lesen eines Zeichens: Der Stream erkennt möglicherweise, dass diesem Zeichen nichts folgt, und setzt eof() .

Wenn du mit einem leeren Stream umgehen willst, ist es trivial: sieh dir etwas aus dem Stream an und fahre fort, wenn du weißt, dass es nicht leer ist:

%Vor%     
Dietmar Kühl 02.11.2012, 23:29
quelle
5

Niemals jemals nach eof suchen.

Das eof -Flag (das mit dem eofbit -Bit-Flag in einem von rdstate() zurückgegebenen Wert übereinstimmt) wird gesetzt, wenn das Dateiende während einer Extraktionsoperation erreicht wird. Wenn es keine Extraktionsoperationen gab, wird eofbit niemals gesetzt, weshalb Ihre erste Überprüfung false zurückgibt.

Jedoch gibt eofbit keinen Hinweis darauf, ob die Operation erfolgreich war. Überprüfen Sie dazu failbit|badbit in rdstate() . failbit bedeutet "es gab einen logischen Fehler" und badbit bedeutet "es gab einen E / A-Fehler". Praktischerweise gibt es eine Funktion fail() , die genau rdstate() & (failbit|badbit) zurückgibt. Noch bequemer gibt es eine Funktion operator bool() , die !fail() zurückgibt. So können Sie Dinge wie while(stream.read(buffer)){ ... tun.

Wenn der Vorgang fehlgeschlagen ist, können Sie eofbit , badbit und failbit separat überprüfen, um herauszufinden, warum ein Fehler aufgetreten ist.

    
n.m. 03.11.2012 00:02
quelle
1

Welche Compiler / Standard-C ++ - Bibliothek verwenden Sie? Ich habe es mit gcc 4.6.3 / 4.7.2 und clang 3.1 versucht, und alle von ihnen funktionierten gut (d. H. Die Behauptung feuert nicht).

Ich denke, du solltest dies als einen Fehler in deiner Toolchain melden, da mein Lesen des Standards mit deiner Intuition übereinstimmt, dass eof () nicht gesetzt werden sollte, solange get () ein Zeichen zurückgeben kann / p>     

rici 02.11.2012 23:37
quelle
1

Es ist kein Fehler in dem Sinne, dass es das beabsichtigte Verhalten ist. Es ist nicht die Absicht, die Sie für eof() testen, bis die Eingabe abgeschlossen ist gescheitert. Sein Hauptzweck ist die Verwendung innerhalb von Extraktionsfunktionen, wo in frühen Implementierungen, die Tatsache, dass std::streambuf::sgetc() return EOF bedeutete nicht, dass es das nächste mal aufgerufen würde: Die Absicht war, dass jederzeit sgetc() EOF (jetzt std::char_traits<>::eof() , dies wäre gespeichert, und der Stream würde keine weiteren Aufrufe an das streambuf machen.

Praktisch gesprochen: Wir brauchen wirklich zwei eof() : eins für den internen Gebrauch, wie oben, und eine andere, die zuverlässig angibt, dass ein Fehler aufgetreten ist das Ende der Datei erreicht haben. So wie es ist, gegeben etwa:

%Vor%

Es gibt keine Möglichkeit festzustellen, dass der Fehler auf einen Formatfehler zurückzuführen ist. anstatt dass der Stream keine Daten mehr hat. In diesem Fall Interner eof sollte wahr sein (weil wir das Ende der Datei gesehen haben, wann vorausschauend, und wir wollen alle weiteren Aufrufe an die streambuf extractor-Funktionen), aber die externe sollte falsch sein, weil dort Daten vorhanden waren (auch nach dem Überspringen der anfänglichen Leerzeichen).

Wenn Sie keine Extractor-Funktion implementieren, sollten Sie das natürlich tun Testen Sie niemals ios_base::eof() , bis Sie tatsächlich einen Eingabefehler hatten. Es war nie die Absicht, dass dies nützliche Informationen liefern würde (was ein Wunder ist, warum sie ios_base::good() -the definiert haben Tatsache, dass es false zurückgibt, wenn eof() bedeutet, dass es nor noch liefern kann zuverlässige Information untin fail() gibt true zurück, zu welchem ​​Zeitpunkt wir weiß, dass es false zurückgibt, also hat es keinen Sinn, es aufzurufen).

Und ich bin mir nicht sicher, was dein Problem ist. Weil der Stream nicht wissen kann im Voraus, was Ihre nächste Eingabe sein wird (z. B. ob es überspringt Leerzeichen oder nicht), kann es im Voraus nicht wissen, ob Ihre nächste Eingabe wird aufgrund des Dateiendes fehlschlagen oder nicht. Die Redewendung ist klar: versuchen Sie die Eingabe, dann testen, ob erfolgreich ist oder nicht. Es gibt kein Andernfalls, weil keine andere Alternative implementiert werden kann. Pascal hat es getan anders, aber eine Datei in Pascal wurde getippt - man konnte nur lesen ein Typ von ihm, so konnte es immer ein Element unter dem vorauslesen hood, und return end of file, wenn dieses Read Ahead fehlgeschlagen. Nicht haben Das vorläufige Ende der Datei ist der Preis, den wir bezahlen, um mehr lesen zu können als ein Typ aus einer Datei.

    
James Kanze 02.11.2012 23:38
quelle
0

Das Verhalten ist etwas subtil. eofbit wird gesetzt, wenn versucht wird, über das Ende der Datei hinaus zu lesen, was aber möglicherweise zum Fehlschlagen der aktuellen Extraktionsoperation führt.

Zum Beispiel:

%Vor%

Wenn die Datei 142<EOF> enthält, wird die Ziffernfolge am Ende der Datei beendet, also wird eofbit gesetzt UND die Extraktion ist erfolgreich. Die Extraktion von j wird nicht versucht, da das Dateiende bereits gefunden wurde.

Wenn die Datei 142 <EOF> enthält, wird die Ziffernfolge durch Leerzeichen beendet (Extraktion von i ist erfolgreich). eofbit ist noch nicht gesetzt, also wird blah >> j ausgeführt, und es wird das Ende der Datei erreichen, ohne Ziffern zu finden, daher wird es fehlschlagen.

Beachten Sie, wie der harmlos aussehende Whitespace am Ende der Datei das Verhalten verändert hat.

    
Ben Voigt 02.11.2012 23:39
quelle

Tags und Links