Właśnie znalazłem komentarz w tej odpowiedzi, który mówi, że używanie iostream::eof
w pętli jest „prawie na pewno złe”. Zasadniczo używam czegoś takiego while(cin>>n)
- co domyślnie sprawdza EOF.
Dlaczego jawne sprawdzanie eofa jest while (!cin.eof())
nieprawidłowe?
Czym różni się od używania scanf("...",...)!=EOF
w C (z którego często korzystam bez problemów)?
scanf(...) != EOF
też nie będzie działać w C, ponieważscanf
zwraca liczbę pól pomyślnie przeanalizowanych i przypisanych. Prawidłowy stan jestscanf(...) < n
gdzien
jest liczba pól w ciągu formatu.EOF
jeśli napotka koniec pliku przed pierwszą konwersją pola (udaną lub nie). Jeśli osiągnięty zostanie koniec pliku między polami, zwróci liczbę pól pomyślnie przekonwertowanych i zapisanych. Co sprawia, że porównanie jestEOF
złe..eof()
po wyjściu z pętli.while(fail)
pętla kończy się zarówno faktyczną awarią, jak i eofem. Zastanów się, czy potrzebujesz 3 ints na iterację (powiedz, że czytasz punkt xyz lub coś takiego), ale błędnie w strumieniu są tylko dwie inty.Odpowiedzi:
Ponieważ
iostream::eof
wróci dopierotrue
po przeczytaniu końca strumienia. To nie nie wskazuje, że następny odczyt będzie koniec strumienia.Zastanów się (i załóż, że następny odczyt będzie na końcu strumienia):
Przeciwko temu:
I na twoje drugie pytanie: Ponieważ
jest taki sam jak
i nie to samo co
źródło
int
s lubstd::string
s lub podobnych bit EOF jest ustawiany, gdy wyodrębnisz go tuż przed końcem, a ekstrakcja dojdzie do końca. Nie musisz czytać ponownie. Powodem, dla którego nie ustawia się podczas odczytu z plików, jest to, że\n
na końcu jest dodatkowy . Omówiłem to w innej odpowiedzi . Czytaniechar
s to inna sprawa, ponieważ wyodrębnia ona tylko jeden na raz i nie osiąga końca.while (!eof())
nie będzie „praca” naint
s lubstd::string
s, gdy wejście jest całkowicie pusty, więc nawet nie wiedząc, że nie ma spływu\n
opieka jest potrzebna."Hello"
(bez końcowych białych znaków lub\n
) istd::string
jest wyodrębniany, wyodrębni litery odH
doo
, przestanie wyodrębniać i wtedy nie ustawiaj bitu EOF. W rzeczywistości ustawiłby bit EOF, ponieważ to EOF zatrzymał ekstrakcję. Mam nadzieję, że wyjaśnię to ludziom.Konkluzja: Przy prawidłowym obchodzeniu się z białą przestrzenią można zastosować następujące metody
eof
(a nawet być bardziej niezawodne niżfail()
przy sprawdzaniu błędów):( Dzięki Tony D za sugestię podkreślenia odpowiedzi. Zobacz jego komentarz poniżej, aby dowiedzieć się, dlaczego jest to bardziej niezawodne ).
Głównym argumentem przeciwko używaniu
eof()
wydaje się być brak ważnej subtelności na temat roli białej przestrzeni. Moja propozycja polega na tym, żeeof()
jawne sprawdzanie nie tylko „nie zawsze jest błędne ” - co wydaje się być nadrzędną opinią w tym i podobnych wątkach SO - ale także przy prawidłowej obsłudze białych znaków zapewnia czystsze i bardziej niezawodne obsługa błędów i jest zawsze poprawnym rozwiązaniem (choć niekoniecznie najkrótszym).Podsumowując, co sugeruje się jako „prawidłowe” zakończenie i kolejność odczytu, należy:
Niepowodzenie spowodowane próbą odczytu poza eof jest traktowane jako warunek zakończenia. Oznacza to, że nie ma łatwego sposobu na odróżnienie udanego strumienia od takiego, który naprawdę zawodzi z powodów innych niż eof. Weź następujące strumienie:
1 2 3 4 5<eof>
1 2 a 3 4 5<eof>
a<eof>
while(in>>data)
kończy się zestawemfailbit
dla wszystkich trzech wejść. W pierwszym i trzecimeofbit
ustawiono również. Tak więc poza pętlą potrzebna jest bardzo brzydka dodatkowa logika, aby odróżnić prawidłowe dane wejściowe (1.) od niewłaściwych (2. i 3.).Podczas gdy weź następujące:
Tutaj,
in.fail()
sprawdza, czy dopóki coś jest do przeczytania, jest to poprawne. Jego celem nie jest zwykły terminator pętli while.Jak dotąd tak dobrze, ale co się stanie, jeśli w strumieniu pozostanie wolna przestrzeń - co wydaje się być głównym problemem
eof()
terminatorowi?Nie musimy rezygnować z obsługi błędów; po prostu zjedz białą przestrzeń:
std::ws
pomija wszelkie potencjalne (zero lub więcej) końcowe miejsce w strumieniu podczas ustawianiaeofbit
, a niefailbit
.in.fail()
Działa więc zgodnie z oczekiwaniami, o ile istnieje co najmniej jedno dane do odczytania. Jeśli dopuszczalne są również całkowicie puste strumienie, wówczas poprawna forma to:Podsumowanie: Prawidłowo skonstruowany
while(!eof)
jest nie tylko możliwy i niepoprawny, ale umożliwia lokalizację danych w zakresie i zapewnia bardziej czyste oddzielenie sprawdzania błędów od działalności w zwykły sposób. To powiedziawszy,while(!fail)
jest bez wątpienia bardziej powszechnym i zwięzłym idiomem i może być preferowane w prostych scenariuszach (pojedyncze dane na typ odczytu).źródło
eofbit
ifailbit
są ustawione, w drugiej tylkofailbit
jest ustawiony. Trzeba tylko przetestować to raz po zakończeniu pętli, nie przy każdej iteracji; opuści pętlę tylko raz, więc musisz tylko sprawdzić, dlaczego raz opuścił pętlę.while (in >> data)
działa dobrze dla wszystkich pustych strumieni.!eof & fail
przeszłą pętlę. Są przypadki, w których nie można na tym polegać. Zobacz powyższy komentarz ( goo.gl/9mXYX ). Tak czy inaczej, nie proponujęeof
-check jako zawsze lepszej alternatywy. Mówię tylko, to jest to możliwy i (w niektórych przypadkach bardziej odpowiedni) sposób, aby to zrobić, zamiast „z pewnością źle!” jak zwykle twierdzi się tutaj w SO.stream >> my_int
gdzie strumień zawiera np. „-”:eofbit
ifailbit
są zestaw. Jest to gorsze niżoperator>>
scenariusz, w którym przeciążenie dostarczone przez użytkownika ma przynajmniej opcję wyczyszczeniaeofbit
przed powrotem, aby pomóc wwhile (s >> x)
użyciu. Mówiąc bardziej ogólnie, ta odpowiedź mogłaby przydać się w czyszczeniu - tylko finałwhile( !(in>>ws).eof() )
jest ogólnie solidny i jest zakopany na końcu.Ponieważ jeśli programiści nie piszą
while(stream >> n)
, prawdopodobnie piszą to:Problem polega na tym, że nie można obejść się
some work on n
bez uprzedniego sprawdzenia, czy odczyt strumienia się powiódł, ponieważ jeśli się nie powiedzie,some work on n
wynik byłby niepożądany.Istotą jest to, że
eofbit
,badbit
albofailbit
są po podejmowana jest próba odczytu z potoku. Jeśli więcstream >> n
zawiedzieeofbit
,badbit
lubfailbit
jest ustawiane natychmiast, więc jest bardziej idiomatyczne, jeśli piszeszwhile (stream >> n)
, ponieważ zwracany obiektstream
konwertuje na,false
jeśli wystąpił błąd odczytu ze strumienia i w konsekwencji pętla zatrzymuje się. Konwertuje się na to,true
czy odczyt się powiódł i pętla trwa.źródło
n
, program może również wpaść w nieskończoną pętlę , jeśli nieudana operacja strumieniowa nie zużywa żadnych danych wejściowych.Inne odpowiedzi wyjaśniły, dlaczego logika jest błędna
while (!stream.eof())
i jak to naprawić. Chcę się skupić na czymś innym:Ogólnie rzecz biorąc, sprawdzanie
eof
tylko jest niepoprawne, ponieważ ekstrakcja strumienia (>>
) może się nie powieść bez uderzania w koniec pliku. Jeśli masz np.int n; cin >> n;
A strumień zawierahello
,h
to nie jest prawidłową cyfrą, więc wyodrębnienie zakończy się niepowodzeniem bez dotarcia do końca danych wejściowych.Ten problem, w połączeniu z ogólnym błędem logicznym sprawdzania stanu strumienia przed próbą odczytu z niego, co oznacza, że dla N elementów wejściowych pętla będzie działać N + 1 razy, prowadzi do następujących symptomów:
Jeśli strumień jest pusty, pętla uruchomi się raz.
>>
zawiedzie (nie ma danych wejściowych do odczytania) i wszystkie zmienne, które miały zostać ustawione (wgstream >> x
), są w rzeczywistości niezainicjowane. Prowadzi to do przetwarzania śmieciowych danych, co może przejawiać się jako nonsensowne wyniki (często ogromne liczby).(Jeśli twoja standardowa biblioteka jest zgodna z C ++ 11, sprawy wyglądają teraz trochę inaczej: błąd nie
>>
ustawia teraz zmiennych numerycznych0
zamiast pozostawiać je niezainicjowane (z wyjątkiemchar
s).)Jeśli strumień nie jest pusty, pętla uruchomi się ponownie po ostatnim prawidłowym wejściu. Ponieważ w ostatniej iteracji wszystkie
>>
operacje kończą się niepowodzeniem, zmienne prawdopodobnie zachowają swoją wartość z poprzedniej iteracji. Może się to objawiać jako „ostatni wiersz jest drukowany dwukrotnie” lub „ostatni rekord wejściowy jest przetwarzany dwa razy”.(Powinno to wyglądać nieco inaczej niż w C ++ 11 (patrz wyżej): Teraz otrzymujesz „widmowy rekord” zera zamiast powtarzanego ostatniego wiersza.)
Jeśli strumień zawiera zniekształcone dane, ale tylko je sprawdzasz
.eof
, powstaje nieskończona pętla.>>
nie uda się wyodrębnić żadnych danych ze strumienia, więc pętla obraca się w miejscu, nie osiągając nigdy końca.Reasumując: Rozwiązaniem jest przetestować sukces
>>
samej operacji, aby nie stosować oddzielną.eof()
metodę:while (stream >> n >> m) { ... }
podobnie jak w C przetestować sukcesscanf
samego połączenia:while (scanf("%d%d", &n, &m) == 2) { ... }
.źródło