Dlaczego ifstream.eof () nie zwraca PRAWDA po odczytaniu ostatniego wiersza pliku?

11

Kiedy początkujący zaczyna czytać strumienie, jego instynkt polega na czytaniu pliku za pomocą pętli, która zwykle wygląda następująco:

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

Kiedy jednak użyłem tego kodu, zauważyłem, że nie zatrzymał się, dopóki nie przeczytał dwukrotnie ostatniego wiersza pliku. Programiści C ++ zauważają, że tak nie powinno się czytać pliku. Zamiast tego zazwyczaj zalecają, aby ktokolwiek musiał odczytać plik, zamiast tego używał pętli:

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

Dlaczego pierwszy fragment kodu zawsze nie działa poprawnie?

moonman239
źródło
Myślałem, że będzie duplikat, ale nie mogę go tutaj znaleźć. Istnieje wiele duplikatów w przepełnieniu stosu.
David Hammen

Odpowiedzi:

4

while (!ifstream.eof())Pętla nie działa, ponieważ strumienie / pliki w C i C ++ nie przewidzieć, kiedy dotarły do końca pliku, lecz raczej wskazują jeśli próbowali odczytać przeszłość koniec pliku.

Jeśli ostatni wiersz pliku kończy się znakiem nowej linii ( \n), wówczas większość operacji odczytu przestanie czytać, gdy napotka ten znak i nie wykryje, że jest to ostatni znak w pliku. Przy następnej akcji odczytu może się zdarzyć, że dodano więcej znaków i że odczyt uda się je wyodrębnić.

Pętla korzystająca z operatora ekstrakcji strumienia ( while (ifstream >> someVar)) działa, ponieważ wynik operatora ekstrakcji strumienia został oceniony na false, jeśli nie można wyodrębnić elementu odpowiedniego typu. Dzieje się tak również, gdy nie ma już znaków do przeczytania.

Bart van Ingen Schenau
źródło
4

Jednak programiści C ++ zauważają, że zawsze zdarza się, że cin.eof () nie zwraca „true”, dopóki podwójny odczyt nie zostanie odczytany.

Tak się nie dzieje. Nie eofbitodgrywa żadnej roli w konwersji na wartość logiczną ( stream::operator bool(lub operator void*starszą wersję c ++)). Zaangażowani są tylko badbiti failbit.

Załóżmy, że czytasz plik zawierający liczby oddzielone spacjami. Pętla oparta na niej cin.eof()nieuchronnie będzie albo błędna, albo będzie pełna iftestów. Nie czytasz do EOF. Czytasz cyfry. Niech twój kod wyrazi tę logikę:

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

Działa to niezależnie od tego, czy ostatni wiersz pliku kończy się na, 0 42\nczy tylko 0 42(nie ma nowego wiersza na końcu ostatniego wiersza w pliku). Jeśli plik kończy się na 0 42\n, ostatni dobry odczyt pobierze wartość 42 i odczyta znacznik końca linii. Zauważ, że znacznik EOF nie został jeszcze odczytany. Funkcja process_valuejest wywoływana za pomocą 42. Następne wywołanie do operatora ekstrakcji strumienia >> czyta EOF, a ponieważ nic nie zostało wyodrębnione, ustawione zostaną zarówno eofbiti failbit.

Załóżmy z drugiej strony, że plik kończy się na 0 42(brak nowej linii na końcu ostatniego wiersza). Ostatni dobry odczyt odzyska wartość 42 kończącą się na znaczniku EOF. Prawdopodobnie chcesz to przetworzyć 42. To dlatego eofbitnie odgrywa roli w operatorze konwersji wartości logicznej strumienia wejściowego. Przy następnym wywołaniu do operatora wydobycia strumienia >> maszyna leżąca pod spodem szybko zauważy, że eofbitjest już ustawiony. To szybko powoduje ustawienie failbit.

Dlaczego pierwszy fragment kodu zawsze nie działa poprawnie?

Ponieważ nie powinieneś sprawdzać EOF jako warunku pętli. Warunek pętli powinien wyrażać to, co próbujesz zrobić, czyli (na przykład) wyodrębnianie liczb ze strumienia.

David Hammen
źródło