how to make fputs function prompt wrong input and ask user to input again...program stopping instead of request for new input

119 Views Asked by At

i'm doing calculation for result. user will input 3 type of result that is pass, fail & invalid. I need to have 1 more checking that is when user input result other than integer value it will be count as wrong input. By using fputs function, how can i ask user to input integer data only ? instead of stopping, program will ask user to input again until user input the correct data.

#include <stdio.h>

int main(void) {
    int test_result;
    int pass;
    int fail;
    int invalid;

    for (int trainee_total; trainee_total < 6; trainee_total++) {
        printf("Enter Result (Pass=1 Fail=2) :");

        if (scanf("%d", &test_result) != 1) {
            fputs("Wrong input\n", stderr);
            return 1;
        }

        if (test_result == 1) {
            pass = pass + 1;
        } else if (test_result == 2) {
            fail = fail + 1;
        } else {
            invalid = invalid + 1;
        }
    }

    printf("The number of participants that passed is :  %d\n", pass);
    printf("The number of participants that failed is :  %d\n", fail);
    printf("Invalid input is :  %d\n", invalid);
}
3

There are 3 best solutions below

2
Jabberwocky On

You probably want something like this:

#include <stdio.h>

// Read an int from the user
//   prompt       Prompte message to display to the user
//   errortext    Text to display upon wrong input
//
int GetIntFromUser(const char *prompt, const char *errortext)
{
  int value;
  while (1) {
    fputs(prompt, stdout);

    // Read the input
    if (scanf("%d", &value) != 1) {
      fprintf(stderr, "%s\n", errortext);
      
      // Clear the input buffer
      int c;
      while ((c = getchar()) != '\n' && c != EOF);
    }
    else {
      break; // Exit the loop if input is valid
    }
  }

  return value;
}


int main(void) {
  int pass = 0;   // initialize to 0 (mandatory)
  int fail = 0;   // initialize to 0 (mandatory)
  int invalid = 0;  // initialize to 0 (mandatory)

  for (int trainee_total = 0; trainee_total < 20; trainee_total++) {
  //                       ^
  //                        initialize to 0, (mandatory)

    // declare test_result here (better style)
    int test_result = GetIntFromUser("Enter Result (Pass=1 Fail=2) :", "Wrong input");

    if (test_result == 1) {
      pass = pass + 1;
    }
    else if (test_result == 2) {
      fail = fail + 1;
    }
    else {
      invalid = invalid + 1;
    }
  }

  printf("The number of participants that passed is :  %d\n", pass);
  printf("The number of participants that failed is :  %d\n", fail);
  printf("Invalid input is :  %d\n", invalid);
}
  • The GetIntFromUser function is inspired from this answer.
  • The explanations are in the comments. The comments with (mandatory) are needed so the program is correct, The comments with (better style) just show that it is a better style.
  • There is maybe still room for improvement for the GetIntFromUser function. If e.g. 11 22 33 is entered the program output looks kind of strange.
0
chux - Reinstate Monica On

Advanced: Real good input is tricky.

Say you create a user input function. How to handle various below cases?

Once we know that, then write the code.


Normal: "123\n" --> nice and clean. Read and return from input function.

Non-numeric: "xyz\n" or "\n" --> Read input line and try again.

Numeric, yet with junk: "123xyz\n" --> Read input line and try again.

Out of range of int: "12345678901234567890\n" --> Read all input line and try again.

End-of-file: Give up reading and return indicate end-of-file.

Input error (rare): Example keyboard disappeared half-way though input. Give up reading and return indicate error.


Consider using fgets() to read a line of user input into a string, then process the string.

Avoid scanf() as it does not handle faulty lines of input well.

0
Some programmer dude On

To better detect and handle errors, I think it's more useful to use fgets to read the input, and strtol to parse the input.

The thing about strtol is that it can detect not only invalid input, but also input that begins with digits and followed by non-digits (like for example "123abc").

Another important thing is that you should really put the input handling in its own function. That means you can use it again from multiple places without having to copy-paste code (which makes it so much harder for maintenance).

Another nice thing with a function is that it's easy to return not only the value that was read and parsed, but also a status saying if the input was valid or not.

Something like this:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <limits.h>
#include <errno.h>

// "Flush" the input buffer, i.e. read everything until either
// end-of-line (the newline) or end-of-file
// Can be used for any input stream
// Returns true if flushed to the end-of-line
// Returns false if flushed to the end-of-file
bool flush_input(FILE *stream)
{
    int c;

    // Read characters until newline or end of file
    while ((c = fgetc(stream)) != '\n' && c != EOF)
    {
        // Empty
    }

    // Returns true if newline, false otherwise
    return c == '\n';
}

// Read an int value from a stream
// This function is line-based, it assumes that the integer is alone on the line
// Pass a pointer to the variable you want to read into using the pointer-to & operator
// Returns true if a valid int value was read
// Returns false otherwise
bool read_integer(FILE *stream, int *destination)
{
    // Don't skimp on memory
    char input[256];

    if (!fgets(input, sizeof input, stream))
    {
        // Failed to read from the input stream
        return false;
    }

    // Now try to parse the input (in base 10)
    char *end;
    long result = strtol(input, &end, 10);

    if (*end == '\0')
    {
        // We filled up the whole buffer with text!
        // That is either a value too big for us to handle
        // Or it contains invalid input (letters, etc.)
        // Either way, we need to flush the stream input and return as a failure
        // (We don't care about what the flush_input function returns)
        (void) flush_input(stream);

        return false;
    }

    if (*end != '\n')
    {
        // The input contains non-digit characters
        // This is a failure because we only allow digit-only input
        (void) flush_input(stream);

        return false;
    }

    if ((result == LONG_MAX || result == LONG_MIN) && errno == ERANGE)
    {
        // The input was a number, but either too large or too small for us to handle
        return false;
    }

    if (sizeof(int) < sizeof(long))
    {
        // The strtol function parses strings into a long integer
        // On this system the types int and long are not the same
        // So we need to add another over-/under-flow check for int
        if (result > INT_MAX || result < INT_MIN)
        {
            return false;
        }
    }

    // If we come to this point, we have valid input and a valid number
    // Set the value and return with success
    *destination = result;
    return true;
}

// This function prompts the user for an integer input, and attempts to read it
// If the input fails for some reason, we retry for a few times
// Pass a pointer to the variable you want to read into using the pointer-to & operator
// Returns true if a valid int value was read
// Returns false otherwise
bool prompt_for_integer(const char *prompt, unsigned retries, int *destination)
{
    for (unsigned try = 0; try < retries; ++try)
    {
        printf("%s", prompt);
        if (read_integer(stdin, destination))
        {
            // Success!
            return true;
        }

        if (feof(stdin))
        {
            // The user gave the end-of-file input sequence
            return false;
        }

        if (ferror(stdin))
        {
            // There's an error in the input, just fail
            return false;
        }

        printf("Failed to read the input, or it was invalid.\n");
    }

    // If the loop ends, we're out of retries and fail
    return false;
}

int main(void)
{
    // The variable we try to fill
    int value;

    // Try to read an integer, but retry at most five times
    if (!prompt_for_integer("Please input an integer: ", 5, &value))
    {
        printf("I'm sorry, but you failed to input a valid integer.\n");
        return EXIT_FAILURE;
    }

    printf("Success! Your input is %d\n", value);
}

I use many different functions to keep functionality separated and easy to reuse and maintain.

The comments should hopefully be enough to explain what happens and why.