Naprawianie strumienia wejścia

2010-05-01 12:46

Kiedy czytamy dane przy pomocy strumienia wejściowego w C++ (basic_istream), wszystko działa pięknie do momentu, gdy zgadzają się one z tym, czego oczekujemy. Ale w rzeczywistych, a nie hello-worldowych programach nie możemy oczekiwać, że np. poniższy kod:

  1. int n;
  2. cin >> n;

w jakiś magiczny sposób zmusi użytkownika, by wpisał liczbę. To samo dotyczy odczytu z plików. Program musi więc być odporny na nieprawidłowe dane.

Łatwo jest na szczęście ocenić, czy takie dane otrzymaliśmy – wystarczy sprawdzić flagi bitowe strumienia, co w najprostszej wersji wygląda po prostu tak:

  1. if (cin) { /* ok */ }

Równie łatwo jest przywrócić je do stanu używalności (metodą clear) i tym samym dać ponownie możliwość odczytu ze strumienia. Wtedy jednak okaże się, że tym, co chcemy odczytać, nie są żadne nowe dane, lecz dokładnie te same, które spowodowały oryginalny błąd.

Ma to sporo sensu – dzięki takiemu zachowaniu może podjąć próbę ich reinterpretacji jako innego typu danych. Zależy to oczywiście od logiki i struktury wejścia, które czytamy. Jeśli jednak rzeczone dane już nas nie interesują i chcielibyśmy raczej powtórzyć próbę odczytania tego samego, musimy się ich jakoś pozbyć.
Da się to zrobić całkiem prosto. Każdy strumień wejścia utrzymuje bowiem bufor odczytu (read buffer), do którego najpierw trafiają znaki z wejścia. Jeżeli okaże się, że ich format nie zgadza się z tym żądanym przez polecenie odczytu, to ów bufor nie jest opróżniany – stąd wynika opisane wyżej zachowanie strumienia. Żeby więc zacząć znowu czytać bezpośrednio z wejścia, bufor ten należy opróżnić. Mamy na szczęście do niego dostęp (metoda rdbuf zwraca na niego wskaźnik), zatem da się to zrobić – w nieco oldschoolowy sposób:

  1. template <typename T> void FixStream(basic_istream<T>& is)
  2. {
  3.     if (!is)
  4.     {
  5.         const int BUF_SIZE = 32;
  6.         T buf[BUF_SIZE];
  7.  
  8.         is.clear();
  9.         basic_streambuf<T>* isBuf = is.rdbuf();
  10.         int toRead = isBuf->in_avail();
  11.         while (toRead > 0)
  12.         {
  13.             int c = toRead > BUF_SIZE ? BUF_SIZE : toRead;
  14.             isBuf->sgetn(buf, c);
  15.             toRead -= c;
  16.         }
  17.     }
  18. }

W skrócie: czytamy z niego po kawałku znaki, aż w końcu nie będzie już niczego… do odczytania :) Pusty bufor sprawia wtedy, że kolejne operacje odczytu ze strumienia będą pobierały dane już bezpośrednio z wejścia.

Be Sociable, Share!
Be Sociable, Share!
Tags: ,
Author: Xion, posted under Programming »


2 comments for post “Naprawianie strumienia wejścia”.
  1. QWRp:
    May 1st, 2010 o 22:19

    A czy nie wystarczy do tego funkcja istream::sync()? Ona także opróżnia bufor. I dla cin działa dokładnie tak samo jak Twoja funkcja (nie wiem jak przy innych strumieniach).

  2. Xion:
    May 1st, 2010 o 23:04

    sync() wywołuje tylko odpowiednią metodę z obiektu bufora leżącego w środku strumienia. W praktycznych sytuacjach (czyli np. dla cin) pewnie rzeczywiście będzie to samo, ale w ogólności “synchronizacja z wejściem” może znaczyć nie tylko to.

Comments are disabled.
 


© 2021 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.