Programowanie w języku C/C++ » FAQ

Zabezpieczenie przed wpisywaniem liter

  • 2013-01-13 21:50
  • 12 komentarzy
  • 2673 odsłony
  • Oceń ten tekst jako pierwszy
Bardzo często na forum pojawia się pytanie: jak zabezpieczyć program przed wpisaniem liter, gdy program oczekuje na liczby.
Najprostsze rozwiązanie w języku C++ wygląda tak:

int zmienna;
while(!(cin>>zmienna)) //dopóki strumień jest w stanie błędu -> dopóki podawane są błędne dane
{
  //ew komunikat błędu
  cin.clear(); //kasowanie flagi błędu strumienia
  cin.sync(); //kasowanie zbędnych znaków z bufora
}
//tutaj na pewno wczytano poprawne dane do zmienna


W języku C nie istnieje równie proste rozwiązanie tego problemu. Można spróbować użyć:

int zmienna;
while(scanf("%d", &zmienna) != 1) //dopóki nie uda się wczytać
{
  //ew. komunikat błędu
  fflush(stdin);
}


Ale nie jest to dobre rozwiązanie, ponieważ jest zależne od implementacji. fflush() nie zawsze powoduje wyczyszczenie bufora!
Rozwiązanie działające w każdej sytuacji (i implementacji) polega na żmudnym wyciągnięciu z stdin zalegających tam, zbędnych znaków (dodatkowa pętla w miejscu fflush())

int zmienna;
while(scanf("%d", &zmienna) != 1) //dopóki nie uda się wczytać
{
  //ew. komunikat błędu
  int c;
  while((c = getchar()) != '\n' && c != EOF); //pętla wyciągająca znaki z bufora
}

Dodaj komentarz

Brak avatara

12 komentarzy

Brak avatara
Agnes 2014-03-06 22:26

Super! Dzieki tyle sie namęczylam nad tym:) Prace dom.  z programowania to nie przelewki. Zwłaszcza jak nie wolno z dodatkowych bibliotek korzystać :( dziekuje :)

Brak avatara
kamil 2013-10-27 11:14

a jak w C++ zabezpieczyc sie przed wpisaniem np.1234abcde on wtedy wczytuje liczbe 1234 ale rowniez wchodzi w while ;/

malizator 2013-01-20 23:58

Drugie rozwiązanie przy kompilowaniu zawsze reaguje dopiero za drugim razem. Jak np trzeba coś wprowadzić z klawiatury. Załóżmy printf("wprowadź wiek\n") pojawia się ten komunikat i jeśli się wpisze jakąkolwiek cyfrę czy liczbę, to za pierwszym razem nic to nie daje, dopiero za drugim odbiera wprowadzone dane. Natomiast jeśli się wpisuje co innego niż cyfry, to działa natychmiast. To tak musi być?

Shalom 2013-01-14 00:28

Drugie rozwiązanie, nawet jeśli jest teoretycznie niepoprawne, to w praktyce wszędzie działa i jest łatwiejsze w napisaniu ;)

Endrju 2013-01-13 21:59

Poprawiłem błędy w drugim kodzie C, mam nadzieję, że jest dobrze.
Nie ma co się spierać o ten EOF. Funkcja getchar wg standardu zwraca int więc int należy od niej odebrać i to na pewno wtedy zadziała jak powinno.

Sugeruję też, żeby drugie (niepoprawne) rozwiązanie usunąć w ogóle.

adf88 2012-04-26 11:43

Zmienna 'c' powinna być typu 'int'. W przeciwnym razie, jeśli 'char' jest domyślnie 'signed', znak 0xFF będzie rozpoznany jako 'EOF' (-1), bo '(signed char)0xFF == EOF'. Jeśli 'char' jest domyślnie 'unsigned' to jeszcze gorzej, 'EOF' będzie puszczone jako zwykły znak, bo '(unsigned char)EOF != EOF'.

Właśnie dlatego 'getchar' zwraca 'int' a nie 'char', aby można było rozróżnić znak (0-255) od EOF (-1). W zasadzie standard nie precyzuje ile wynosi 'EOF', nie koniecznie -1. W każdym bądź razie ile by to nie były to rzutowane na 'char' wygeneruje jeden z kodów ASCII. Standard gwarantuje, że można porównać wynik 'getchar' do EOF. Jeśli ten wynik rzutujemy na 'char' to tracimy precyzję i porównanie nie musi się powieść.

Shalom 2011-04-12 01:13

@Azrael_Valedhel nie do końca się zgodzę. Dokumentacja twierdzi:
"This effectively means that the unread characters in the buffer are discarded"
Ba, nawet jako przykład użycia pokazane jest jak czyścić strumień cin.
A co do działania fflush to jest wyraźnie napisane że nie należy go używać.

Azrael_Valedhel 2011-04-11 22:31

Oj do poprawy ten wpis, istream::sync() różnie działa zależnie od implementacji i od tego na jakim strumieniu jest wywołane, a fflush(stdin) to niezdefiniowane zachowanie.

Janek566 2011-03-07 15:53

mogłbyś dodać kod w delphi :P

Olivia 2010-08-23 21:10

Hm. A niedawno czytałam, że to błąd. Dzięki za wyprostowanie.

Shalom 2010-08-23 21:08

"In the C Standard Library, character read functions such as getchar return a value equal to the symbolic value (macro) EOF to indicate that an end-of-file condition has occurred"
Parafrazując: EOF w powyższym kodzie to nie jest wartość zmiennej char, a pewne makro, które ma "specjalną wartość". Funkcja getchar() sprytnie nie zwraca nam "znaku eof" (bo takowego w ascii nie ma) a jedynie ową "specjalną wartość" kiedy natrafi na prawdziwego eof'a. Stąd też kod jest poprawny ;)

Olivia 2010-08-23 20:07

Rozwiązanie c chyba nie jest zbyt dobre.  char nie przechowa EOF.