2016-07-11 88 views
5

Ho una variabile "minuti" che mi piacerebbe che l'utente inserisse un numero positivo in.inserendo una stringa in scanf con una condizione while <0 causa il ciclo infinito

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); 
    } 
} 

Funziona come previsto per i numeri. Se minuti> = 0, lo accetta e se i minuti < 0 continua a chiedere. Se inserisco una stringa, loop infinitamente

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

e non mi dà mai la possibilità di inserire un nuovo valore. Perché è questo e come posso risolverlo? Grazie!

+0

Cosa intendi con questo programma? Cosa intendi con minuti negativi e positivi? – user3078414

+0

Si noti che 'scanf' non è il modo più sicuro per ottenere input in primo luogo, specialmente se si vuole gestire le diverse possibilità di input. Vedere la risposta accettata [qui] (http://stackoverflow.com/questions/2430303/disadvantages-of-scanf) – iRove

+0

Perché il titolo è formulato come se si trattasse di un bug in C? – cat

risposta

6

Se non si immette un valore numerico, qualsiasi valore digitato rimane nel buffer di input. È possibile verificare ciò leggendo il valore di ritorno di scanf, che indica il numero di elementi letti. Se è 0, è possibile utilizzare getchar per leggere i caratteri fino alla riga successiva successiva per svuotare il buffer.

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

Dove dichiarate e impostate 'rval' nel primo passaggio? – user3078414

+0

@ user3078414 Manca una copia/incolla lì. Fisso. – dbush

+0

Ottima soluzione. +1 per l'uso di 'getchar', che è il modo compatibile con POSIX per cancellare il buffer –

2

L'identificatore %f conversione dice scanf a smettere di leggere in ingresso non appena vede un personaggio che non fa parte di una virgola mobile legale costante (vale a dire, qualcosa che non è una cifra, punto decimale, o segno). Il carattere errato viene lasciato nello stream di input, quindi la chiamata successiva a scanf non riesce e il successivo e il successivo ecc.

È necessario controllare sempre il valore restituito di scanf - ti dirà quanti elementi sono stati letto e assegnato con successo dal flusso di input. In questo caso, ti aspetti un singolo elemento, quindi dovresti ottenere un valore di ritorno di 1. Se ottieni un valore di ritorno pari a 0, significa che l'input non è un valore in virgola mobile e che l'input errato deve essere chiarito in qualche modo. Ecco una possibile soluzione:

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

L'unico problema con questo è che scanf è una specie di muto, e se si digita qualcosa come 123fgh, verrà convertito e assegnare la 123 lasciando fgh nel flusso in entrata; probabilmente vorresti rifiutare completamente quell'intero input.

Una soluzione è quella di leggere l'input come testo, e poi convertirlo con 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 
    } 
} 

Questo ha il vantaggio di non lasciare merda nel flusso di input.

+0

Nota: questo codice accetta input come "" 123 abc "' come input valido. Tuttavia usare 'fgets()' è un buon approccio. – chux