Prócz flagi stanu formatowania, każdy strumień posiada słowo stanu zawierające informacje o stanie i o ewentualnych błędach związanych z operacjami we/wy na tym strumieniu. Tak jak flaga stanu formatowania, słowo stanu strumienia ma postać całkowitoliczbowej zmiennej (typu iostate).
Programista ma możliwość badania aktualnego stanu strumienia za pomocą metod wywoływanych na rzecz obiektów-strumieni.
Można też badać stan strumienia bezpośrednio w warunkach logicznych instrukcji if, for, while itd. W takich przypadkach wartość strumienia zostanie przekonwertowana automatycznie dzięki przeciążeniu w klasie ios operatora '!' oraz operatora konwersji do typu void*.
W wyrażeniach typu
if ( strm ) ...wartość strm zostanie przekonwertowana do wartości wskaźnika pustego NULL (odpowiadającego false), jeśli stan strumienia jest „zły”, to znaczy, jeśli metoda fail wywołana na rzecz tego strumienia dałaby true (patrz dalej). W przeciwnym przypadku, czyli gdy stan strumienia jest „dobry”, zwrócona zostanie wartość niezerowa, odpowiadająca true. Podobnie w wyrażeniu
if ( !strm ) ...operator '!' zastosowany do strumienia zwróci wartość logiczną true wtedy i tylko wtedy, gdy metoda fail zwróciłaby true.
Słowo stanu zapewnia nam jednak więcej informacji. Tak jak flagę stanu formatowania można składać (lub rozkładać na czynniki pierwsze...) za pomocą nazwanych stałych, tak i słowo stanu strumienia można przez „ORowanie” składać z predefiniowanych stałych typu iostate.
ios::badbit — Strumień jest zniszczony; nie wiadomo, czy ostatnia operacja na nim powiodła się. Następna na pewno nie powiedzie się.
ios::eofbit — Wykonano próbę dostępu do danych poza końcem strumienia (pliku).
ios::failbit — Następna operacja nie powiedzie się, ale strumień nie jest zniszczony, w szczególności nie zgubiono żadnych znaków.
ios::goodbit — Strumień jest „zdrowy”. Wartością goodbit jest 0.
Stan strumienia można badać za pomocą funkcji składowych (metod) klasy ios
zwracających w postaci wartości logicznej ustawienie bitów ios::badbit, ios::eofbit, ios::failbit i ios::goodbit w słowie stanu strumienia.Są też metody pozwalające manipulować słowem stanu strumienia „ręcznie” — nie tylko odczytywać go, ale i modyfikować.
iostate rdstate() — Zwraca słowo stanu w postaci zmiennej typu iostate.
void clear(iostate stan = ios::goodbit) — Zeruje słowo stanu, co odpowiada ustawieniu stanu na good. Następnie ustawia znacznik stan, który musi być jedną ze stałych ios::badbit, ios::failbit itd. Domyślną wartością jest ios::goodbit, a zatem wywołanie bez argumentu „naprawia” strumień — patrz przykład w programie plikrw.cpp.
void setstate(iostate stan) — Dodaje (przez „ORowanie”) znacznik stan do słowa stanu. Argument musi być jedną ze stałych ios::badbit, ios::failbit itd. Równoważne wywołaniu ' clear( rdstate() | ios::stan )'.
Generalnie, stan strumienia należy w poważnych programach
sprawdzać po każdej operacji. Jeśli stan strumienia jest
„zły”, a więc funkcja
fail,
bad
lub
eof
zwraca
true, to każda następna
operacja we/wy na tym strumieniu jest ignorowana, natomiast
nie jest zgłaszany żaden błąd!
Rozpatrzmy przykład:
1. #include <fstream> 2. #include <iostream> 3. using namespace std; 4. 5. int main() { 6. const int DIM = 80; 7. char name[DIM]; 8. ifstream inplik; 9. double x; 10. 11. do { 12. cout << "Plik wejsciowy: "; 13. cin.getline(name, DIM); 14. 15. inplik.clear(); 16. inplik.open(name); 17. } while (!inplik); 18. 19. cout << "Plik = " << name << endl; 20. inplik.close(); 21. 22. do { 23. if (!cin) { 24. // wazna kolejnosc! 25. cin.clear(); 26. cin.ignore(1024,'\n'); 27. }; 28. cout << "Podaj liczbe: "; 29. cin >> x; 30. } while (!cin); 31. 32. cout << "Liczba = " << x << endl; 33. }
Bardziej subtelna jest pętla w liniach 22-30, której zadaniem jest wczytanie liczby. W linii 29 usiłujemy wczytać liczbę na zmienną x. Jeśli się to nie powiedzie, bo na przykład użytkownik wpisał zamiast liczby litery, to stan strumienia cin będzie bad. Zatoczymy zatem pętlę i podejmiemy kolejną próbę. Ale ponieważ stan strumienia jest zły, operacje wejścia na tym strumieniu będą ignorowane! Zatem musimy naprawić stan, co czynimy w linii 25. To jeszcze nie wszystko. Ponieważ bufor strumienia nie został „wyczytany”, gdybyśmy ograniczyli się do naprawy strumienia, to następna operacja będzie czytać nie wprowadzoną, być może tym razem prawidłowo, liczbę, ale „śmieci” pozostałe w buforze z poprzedniej, nieudanej próby. I tak w nieskończoność będziemy czytać te same śmieci! Zatem musimy je usunąć przez wywołanie ignore w linii 26. Teraz program działa prawidłowo (zakładamy, że istnieje w aktualnym katalogu plik val.dat):
Plik wejsciowy: val.txt Plik wejsciowy: val Plik wejsciowy: val.dat Plik = val.dat Podaj liczbe: s12 Podaj liczbe: @ Podaj liczbe: 12 Liczba = 12Zauważmy też, że ważna jest kolejność naprawiania strumienia i wywoływania ignore z linii 25 i 26. Gdybyśmy wywołali ignore przed naprawieniem strumienia, to wywołanie to zostałoby zignorowane, bo przecież jest to operacja wejścia/wyjścia, a stan strumienia jest bad. A więc „śmieci” pozostałyby w strumieniu i znów wpadlibyśmy w nieskończoną pętlę, nie mogąc pozbyć się błędnych danych. Po odwróceniu kolejności linii 25 i 26 otrzymalibyśmy zatem:
Plik wejsciowy: val.dat Plik = val.dat Podaj liczbe: s12 Podaj liczbe: Podaj liczbe: Podaj liczbe: Podaj li czbe: Podaj liczbe: Podaj liczbe:gdzie wykonanie musieliśmy przerwać wciskając Ctrl-C.
Niestety, prawidłowa i pełna obsługa wszystkich możliwych błędów operacji wejścia i wyjścia to zadanie bardzo trudne i pracochłonne. Kod z tym związany może stanowić znaczną część (czasem większość!) całego programu.
T.R. Werner, 21 lutego 2016; 20:17