Home > Net >  While Loop doesn't break when error occurrs
While Loop doesn't break when error occurrs

Time:01-10

The while loop below breaks when a letter is added. I would like it to continue to loop even if a letter is added, as well as if a negative number is added. Here's my current code:

float monthpay;

printf("Please enter your monthly wage:\t");
    scanf("%f", &monthpay);
    while (monthpay <= 0)
    {
        printf("\nThat was invalid");
        printf("\n Please try again: ");
        scanf("%f", &monthpay);

    }
printf("%.2f", monthpay);

I was trying to make a loop that would loop if a letter was added by mistake.

CodePudding user response:

scanf() returns number of input items that were successfully matched and assigned. If you don't check the return value (rv) your variable (monthpay) may be uninitialized, and it's undefined behavior to use it.

void flush() {
   for(;;) {
     char ch;
     int rv = scanf("%c", &ch);
     if(rv == EOF || rv == 1 && ch == '\n') return;
   }
}

printf("Please enter your monthly wage:\t");
float monthpay;
for(;;) {
    int rv = scanf("%f", &monthpay);
    if(rv == EOF)
        return;
    if(rv == 1)
        break;
    printf("\nThat was invalid");
    printf("\n Please try again: ");
    flush();
}
printf("%.2f", monthpay);

CodePudding user response:

After trying to scan a float, scan and discard whitespace except for newline, scan discard and count any remaining characters that are not a newline, scan a newline.
The %n specifier will provide a count of the characters processed by the scan up to the specifier.
The asterisk in the scanset %*[ \t\f\v] tells scanf to discard the matching characters. The carat in the scanset %*[^\n] tells scanf to process the non-matching characters so everything that is not a newline will be discarded. %1[\n] tells scanf to scan one character and it must be a newline. If a float is entered followed only by whitespace, the loop will exit as scanned will be 2 and extra will be 0.

#include <stdio.h>

int main ( void) {
    char newline[2] = "";
    int extra = 0;
    int scanned = 0;
    float monthpay = 0.0;

    do {
        printf ( "enter monthly pay\n");
        scanned = scanf ( "%f", &monthpay);
        if ( scanned == EOF) {
            return 1;
        }
        extra = 0;
        scanf ( "%*[ \f\t\v]"); // scan and discard whitespace except newline
        scanf ( "%*[^\n]%n", &extra); // scan discard and count non-whitespace
        scanned  = scanf ( "%1[\n]", newline);
    } while ( monthpay < 0 || scanned != 2 || extra != 0);

    return 0;
}

CodePudding user response:

You should always check the return value of scanf to determine whether the input was successfully converted, before you attempt to use the result.

However, for line-based user input, using the function scanf is generally not advisable, because its behavior is not intuitive. It usually does not consume an entire line of input, which can cause all kinds of trouble. For example, if the user enters 6abc\n, then scanf will match the 6 as valid input, but leave abc\n on the input stream.

For this reason, it is generally better to always read an entire line of input at once as a string, for example using the function fgets. You can then use the function strtof to attempt to convert the string to a number.

Here is an example, which performs full input validation:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

float get_float_from_user( const char *prompt );

int main( void )
{
    float monthpay;

    //loop forever until user enters a valid number
    for (;;) //infinite loop, equivalent to while(1)
    {
        monthpay = get_float_from_user( "Please enter your monthly wage: ");
        if ( monthpay > 0 )
        {
            //input is ok, so we can break out of the loop
            break;
        }

        printf( "Please enter a positive number!\n" );
    }

    printf( "Input was valid. You entered: %.2f\n", monthpay );

    return 0;
}

float get_float_from_user( const char *prompt )
{
    //loop forever until user enters a valid number
    for (;;)
    {
        char buffer[1024], *p;
        float f;

        //prompt user for input
        fputs( prompt, stdout );

        //get one line of input from input stream
        if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
        {
            fprintf( stderr, "Unrecoverable input error!\n" );
            exit( EXIT_FAILURE );
        }

        //make sure that entire line was read in (i.e. that
        //the buffer was not too small)
        if ( strchr( buffer, '\n' ) == NULL && !feof( stdin ) )
        {
            int c;

            printf( "Line input was too long!\n" );

            //discard remainder of line
            do
            {
                c = getchar();

                if ( c == EOF )
                {
                    fprintf( stderr, "Unrecoverable error reading from input!\n" );
                    exit( EXIT_FAILURE );
                }

            } while ( c != '\n' );

            continue;
        }

        //attempt to convert string to number
        errno = 0;
        f = strtof( buffer, &p );
        if ( p == buffer )
        {
            printf( "Error converting string to number!\n" );
            continue;
        }

        //make sure that number is representable as a "float"
        if ( errno == ERANGE )
        {
            printf( "Number out of range error!\n" );
            continue;
        }

        //make sure that remainder of line contains only whitespace,
        //so that input such as "6abc" gets rejected
        for ( ; *p != '\0'; p   )
        {
            if ( !isspace( (unsigned char)*p ) )
            {
                printf( "Unexpected input encountered!\n" );

                //cannot use `continue` here, because that would go to
                //the next iteration of the innermost loop, but we
                //want to go to the next iteration of the outer loop
                goto continue_outer_loop;
            }
        }

        return f;

    continue_outer_loop:
        continue;
    }
}

This program has the following behavior:

Please enter your monthly wage: abc
Error converting string to number!
Please enter your monthly wage: 6abc
Unexpected input encountered!
Please enter your monthly wage: 6e 2000
Number out of range error!
Please enter your monthly wage: -5
Please enter a positive number!
Please enter your monthly wage: 0
Please enter a positive number!
Please enter your monthly wage: 6.3
Input was valid. You entered: 6.30
  • Related