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):
while( !(in>>std::ws).eof() ) {
int data;
in >> data;
if ( in.fail() ) /* handle with break or throw */;
// now use data
}
( 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, że eof()
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:
int data;
while(in >> data) { /* ... */ }
// which is equivalent to
while( !(in >> data).fail() ) { /* ... */ }
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ę zestawem failbit
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:
while( !in.eof() )
{
int data;
in >> data;
if ( in.fail() ) /* handle with break or throw */;
// now use data
}
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ń:
while( !in.eof() )
{
int data;
in >> data >> ws; // eat whitespace with std::ws
if ( in.fail() ) /* handle with break or throw */;
// now use data
}
std::ws
pomija wszelkie potencjalne (zero lub więcej) końcowe miejsce w strumieniu podczas ustawiania eofbit
, 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:
while( !(in>>ws).eof() )
{
int data;
in >> data;
if ( in.fail() ) /* handle with break or throw */;
/* this will never fire if the eof is reached cleanly */
// now use data
}
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).
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.