Pollot
Pollot

Reputation: 23

Checking if user-provided value is integer and if not asking for a correct value

I am looking for some help with scanned user-provided values. In my code below I want to check if every entered value is integer (ca, ta, dd, gs), and if not - loop a message about it to the point where it will be. I saw few old questions about it, but I couldn't get it to work anyways. I am fairly new to programming, so I don't know much. I appreciate every answer.

int ca; // Current altitude
int ta; // Target altitude
int dd; // Distance for descent/climb
int gs; // Ground speed`

char cont; // variable to continue or close program

do
{
    printf("Type your current altitude: ");
    scanf("%i", &ca);

    printf("\nType your target altitude: ");
    scanf("%i", &ta);

    while(ca == ta) // If user entered same target altitude as current one, message will prompt him to type it again
    {
        printf("Target altitude needs to be different than your current one! Type it again: ");
        scanf("%i", &ta);
    }

    printf("\nType distance for descent/climb: ");
    scanf("%i", &dd);

    printf("\nType your ground speed: ");
    scanf("%i", &gs);

    int ad = ta - ca; // Calculates altitude difference - how much feet aircraft needs to descent or climb

    float time = dd*1.0 / gs*1.0; // v = s / t  =>  t = s / v  - Calculates how much time aircraft has to descent or climb (time in hours) | variables are multiplied by 1.0 to receive float in result

    int fpm = ad / (time*60); // Calculates FPM needed to descent in that time | time is multiplied by 60 in order to receive time in minutes

    if(fpm > 0)
    {
       printf("\nAircraft needs to climb at rate of %i FPM (doesn't include capturing altitude)\n", fpm);
    }
    else
    {
        printf("\nAircraft needs to descent at rate of %i FPM (doesn't include capturing altitude)\n", fpm);
    }

    printf("\nDo you want to run a program again? (y/n) ");
    scanf(" %c", &cont);

    while(cont != 'y' && cont != 'Y' && cont != 'n' && cont != 'N') // If user typed different character than y or n, message will prompt him to type it again
    {
        printf("\nType n or y: ");
        scanf(" %c", &cont);
    }

    printf("\n");

}
while(cont == 'Y' || cont == 'y');

return 0;

Upvotes: 2

Views: 160

Answers (3)

Luca Polito
Luca Polito

Reputation: 2892

Making sure that user's input is really an integer is quite tricky in C, and scanf() isn't gonna help you much (even if used correctly, it still won't warn you about integer overflows, for instance).

One alternative is to read a line using fgets() and then parse it with atoi(), but fgets() will give you headaches when input line is longer than maximum length. Also, atoi() won't let you know if the number is valid or not (technically it does catch some errors, but you cannot distinguish them from 0), so it may be replaced with strtol().

Therefore, this is a possible approach:

#include <stdio.h> // printf(), fflush(), fgetc(), EOF
#include <errno.h> // errno
#include <stdlib.h> // EXIT_SUCCESS, EXIT_FAILURE, strtol()

#define MAX_LINE_SIZE 100 // this is the maximum size that a user's input line can be

// FUNCTION: int get_line_and_discard(char[], int);
// this function will help us read a line from `stdin`, and if user's line is longer than `str_size - 1`, the function will discard all user input until '\n'
// this will help us avoid some bugs around the use of `fgets()`
// RETURN VALUE: -1 in case of EOF or other fatal errors, 0 in case user's line is too long or empty, otherwise the line's length
// NOTE: `str` will not contain the newline and will always be NUL-terminated in case return value is >= 0
int get_line_and_discard(char str[], int str_size) {
  if (str_size < 1) {
    return -1;
  }

  int count = 0;

  while (1) {
    int c = fgetc(stdin);

    if (c == EOF) {
      return -1;
    }

    if (c == '\n') {
      str[count] = '\0';
      return count;
    }

    if (count >= str_size - 1) {
      while (1) {
        int temp = fgetc(stdin);
        if (temp == '\n' || temp == EOF) {
          break;
        }
      }
      str[0] = '\0';
      return 0;
    }

    str[count] = (char) c;

    count += 1;
  }
}

// FUNCTION: int get_number_from_user(const char*, long*);
// this function will print `ask_str` and will read a `long` integer from user
// RETURN VALUE: -1 in case of fatal errors (e.g.: EOF), 0 in case number is not valid, 1 in case of success
int get_number_from_user(const char *ask_str, long* result) {
  // print the `ask_str`
  printf("%s", ask_str);
  fflush(stdout);

  char str[MAX_LINE_SIZE];

  // read a line from `stdin`
  int slen = get_line_and_discard(str, MAX_LINE_SIZE);

  // in case of EOF or fatal errors
  if (slen < 0) {
    return -1;
  }

  // in case line is empty
  if (slen < 1) {
    return 0;
  }

  // now let's use `strtol()` to parse the number
  char *str_end;
  errno = 0;
  *result = strtol(str, &str_end, 10);

  // check that `strtol()` has not failed
  if (errno != 0 || str_end != &str[slen] || str == str_end) {
    return 0;
  }

  return 1;
}

And this is how you use it:

int main(void) {
  long res;

  // ask the user to input a number until the number is valid
  while (1) {
    int r = get_number_from_user("Insert number: ", &res);

    if (r < 0) {
      printf("ERROR: fatal error\n");
      return EXIT_FAILURE;
    }

    if (r == 0) {
      printf("ERROR: invalid number, please retry.\n");
      continue;
    }

    break;
  }

  printf("You have input: %ld\n", res);

  return EXIT_SUCCESS;
}

Upvotes: 1

Moshe
Moshe

Reputation: 35

You can do it easily if you declare your variables as a string, then later if it is correct, you can change it to an integer.

char user_input[100];
scanf("%s", &user_input);
while (!is_int(user_input))
{
    scanf("%s", &user_input);
}
int number = atoi(user_input);

The function:

int is_int(char* user_input)
{
    int i = 0;
    while (user_input[i])
    {
        int num = user_input[i];
        if (!(num >= '0' && num <= '9'))
        {
            return 0;
        }
        i++;
    }
    return 1;
}

Upvotes: 0

chux
chux

Reputation: 154218

I want to check if every entered value is integer

Instead of printf(...); scanf("%i", ...);, form a helper function.

int get_int(const char *prompt) {
  int value = 0;
  for (;;) {
    fputs(prompt, stdout);
    int count = scanf("%i", &value);
    if (count == 1) {
      return value;
    } 
    if (count == EOF) { // No more input
      return 0; // or some other default value.
    } 
    // No numeric input, consume rest of the line
    puts("value is not integer");
    int ch;
    while ((ch = getchar()) != '\n' && ch != EOF) {
      ;
    }
  }
}


....
// printf("Type your current altitude: ");
// scanf("%i", &ca);
ca = get_int("Type your current altitude: ");

// printf("\nType your target altitude: ");
// scanf("%i", &ta);
ta = get_int("Type your target altitude: ");

More elaborate input error checking possible with fgets(), strtol().

Upvotes: 1

Related Questions