2016-07-11 28 views
5

Mam zmienną "minutes", którą chciałbym, aby użytkownik wprowadził liczbę dodatnią.wprowadzanie ciągu znaków do scanf z czasem warunek <0 powoduje nieskończoną pętlę

int main(void) 
{ 
    float minutes; 
    minutes = -1; 
    printf("Find out how many bottles worth of water your showers use!\n"); 
    printf("How many minutes do you spend in the shower? "); 
    scanf("%f", &minutes); 
    while(minutes < 0) 
    { 
     printf("Please enter a positive number: "); 
     scanf("%f", &minutes); 
    } 
} 

Działa zgodnie z przeznaczeniem dla numerów. Jeśli minuty> = 0, akceptuje je, a jeśli minuty < 0 ciągle zadaje. Jeśli wprowadzę ciąg, będzie on nieskończenie pętli

printf("Please enter a positive number: "); 

i nigdy nie daje mi szansy na wprowadzenie nowej wartości. Dlaczego tak jest i jak mogę to naprawić? Dzięki!

+0

Co zamierzacie z tym programem? Co masz na myśli z minutami negatywnymi i pozytywnymi? – user3078414

+0

Zauważ, że 'scanf' nie jest najbezpieczniejszym sposobem uzyskania danych wejściowych, szczególnie, gdy chcesz obsłużyć różne możliwości wprowadzania danych. Zobacz zaakceptowaną odpowiedź [tutaj] (http://stackoverflow.com/questions/2430303/disadvantages-of-scanf) – iRove

+0

Dlaczego tytuł jest sformułowany tak, jakby był to błąd w C? – cat

Odpowiedz

6

Jeśli nie wprowadzisz wartości liczbowej, cokolwiek zostanie wpisane, pozostanie w buforze wejściowym. Możesz to sprawdzić, odczytując wartość zwracaną przez scanf, która informuje o liczbie odczytanych pozycji. Jeśli jest 0, możesz użyć getchar, aby odczytywać znaki, aż do następnego znaku nowej linii, aby opróżnić bufor.

int main(void) 
{ 
    int rval, c; 
    float minutes; 
    minutes = -1; 
    printf("Find out how many bottles worth of water your showers use!\n"); 
    printf("How many minutes do you spend in the shower? "); 
    rval = scanf("%f", &minutes); 
    if (rval == 0) { 
     while (((c = getchar()) != '\n') && (c != EOF)); 
    } 
    while(minutes < 0) 
    { 
     printf("Please enter a positive number: "); 
     rval = scanf("%f", &minutes); 
     if (rval == 0) { 
      while (((c = getchar()) != '\n') && (c != EOF)); 
     } 
    } 
} 
+0

Gdzie zadeklarujesz i ustawisz "rval" w pierwszym przejściu? – user3078414

+0

@ user3078414 Nie można tam skopiować/wkleić. Naprawiony. – dbush

+0

Świetne rozwiązanie. +1 dla używania 'getchar', który jest zgodnym z POSIX sposobem czyszczenia bufora –

2

specyfikatorem %f konwersja mówi scanf przestać czytać wejście tak szybko, jak to widzi znak, który nie jest częścią prawnej zmiennoprzecinkowej stałej (czyli coś, co nie jest cyfra, przecinek, lub znak). Ten zły znak zostaje pozostawiony w strumieniu wejściowym, więc następne wywołanie do scanf kończy się niepowodzeniem, a następne i następne itd.

Powinieneś zawsze sprawdzić wartość zwracaną przez scanf - dowiesz się, ile elementów było poprawnie odczytane i przypisane ze strumienia wejściowego. W tym przypadku spodziewamy się pojedynczego elementu, więc powinieneś otrzymać wartość zwracaną równą 1. Jeśli otrzymasz wartość 0, oznacza to, że dane wejściowe nie są poprawną wartością zmiennoprzecinkową i że złe wejście musi być jakoś oczyszczonym. Oto jeden z możliwych rozwiązań:

if (scanf("%f", &minutes) == 1) 
{ 
    // process minutes as normal 
} 
else 
{ 
    // clear everything up to the next whitespace character 
    while (!isspace(getchar())) 
    ; // empty loop 
} 

Jedyny problem polega na tym, że scanf jest trochę głupie, a jeśli wpiszesz coś jak 123fgh, to konwersję i przypisać 123 pozostawiając fgh w strumieniu wejściowym; prawdopodobnie chcesz całkowicie odrzucić cały wkład.

Jednym z rozwiązań jest odczytać dane wejściowe jako tekst, a następnie przekształcić go za pomocą strtod:

char buffer[BUFSIZE]; // where BUFSIZE is large enough to handle expected input 
... 
if (fgets(buffer, sizeof buffer, stdin)) 
{ 
    char *chk; // chk will point to the first character *not* converted; if 
      // it's anything other than whitespace or the string terminator, 
      // then the input was not a valid floating-point value. 
    double tmp = strtod(buffer, &chk); 
    if (isspace(*chk) || *chk == 0) 
    { 
    minutes = tmp; 
    } 
    else 
    { 
    // input was not a proper floating point value 
    } 
} 

Ma to tę zaletę, nie pozostawiając bzdura w strumieniu wejściowym.

+0

Uwaga: Ten kod akceptowałby wejście takie jak "123 abc" 'jako prawidłowe dane wejściowe, ale używanie' fgets() 'jest dobrym podejściem. – chux