2

I have to get user input but only a single character either 1-3. If the user inputs anything else, an error message should return. I have the following, which is a portion of my code:

char val = 'i';
while (1) {
   if (val == 'i') {
      printf("Enter option: (1/2/3)\n");
      scanf(" %c", &num);
   }
   else if (num == '1') {
      //do things
   }
   else if (num == '2') {
      //do things
   }
   else if (num == '3') {
      //do things
   }
   else {
      // return error
   }

My problem is, I'm taking in a %c so a single char, but the user might input something such as 123 and it would still take in the first char, in this case 1. The else statement at the end doesn't take care of all other alternate cases.

Is there a way to limit the user input to a single char between a given range? I've also tried fgets() and strncmp() but had problems with those too.

kelp99
  • 71
  • 2
  • 9
  • Always validate the return of `scanf` and then following `scanf` add `for (int c = getchar(); c != '\n' && c != EOF; c = getchar()) {}` to remove any additional characters (including the `'\n'` generated by pressing `[Enter]`) **before** your next input attempt. You can also use `fgets` (e.g. `fgets (line, sizeof line, stdin)`) to read the entire line and then simply check if `*line - '0'` is `1, 2 or 3` and user a large enough buffer to insure it can contain the entire input (and validate that it did) – David C. Rankin Jul 25 '18 at 21:53
  • Have you tried getchar instead? – Andrew Truckle Jul 25 '18 at 21:53
  • Possible duplicate of [What is Equivalent to getch() & getche() in Linux?](https://stackoverflow.com/questions/7469139/what-is-equivalent-to-getch-getche-in-linux) – Jay Elston Jul 25 '18 at 21:53
  • Also look at: https://stackoverflow.com/questions/32781937/getting-a-single-character-without-pressing-enter – Jay Elston Jul 25 '18 at 21:55
  • You may want to detect a keyboard event instead https://stackoverflow.com/questions/220818/detect-keyboard-event-in-c – ZenJ Jul 25 '18 at 22:57

2 Answers2

3

Is there a way to limit the user input to a single char between a given range? I've also tried fgets()

Yes (but it is your responsibility to isolate and use the 1st character typed and discard the rest)

You can take the input any way you like, e.g. scanf, getchar, fgets, etc.., but it is your responsibility to validate every aspect of the input. E.g.,

  1. did I get a valid character?,
  2. was it in the required range?,
  3. did the user cancel input by generating a manual EOF (e.g. Ctrl+d on Linux or Ctrl+z on windoze, but see: CTRL+Z does not generate EOF in Windows 10),
  4. did the user enter more than the number of characters requested (or did a cat step on the keyboard)?, and finally
  5. have I emptied the input buffer (e.g. stdin) before I attempt my next input?

While you can use scanf or getchar, it is probably easier for new C programmers to use fgets which will read upto and include the trailing '\n' in the buffer filled. (you may need multiple reads with a fixed size buffer to consume a line that exceeds the buffer length -- but this is easily handled with a loop).

Also, since you will use a character array as the buffer (which is converted to a pointer to the first element (character) on access), you can simply dereference the buffer to obtain the first-character to validate it is 1-3.

Putting it altogether, it sounds like you are asking to do something similar to the following:

#include <stdio.h>
#include <string.h>

#define MAXC 1024   /* do NOT skimp on buffer size */

int main (void) {

    char buf[MAXC] = "";

    puts ("press [Enter] alone or generate manual EOF to exit.\n");
    while (1) {
        size_t len = 0;
        fputs ("enter single char 1-3: ", stdout);
        if (!fgets (buf, sizeof buf, stdin)) {  /* validate read */
            /* manual EOF */
            fputs ("(user canceled input)\n", stderr);
            break;
        }
        if (*buf == '\n')       /* check for [Enter] alone */
            break;
        if (*buf < '1' || '3' < *buf)   /* if not 1-3, error */
            fprintf (stderr, "  error: invalid input '%c'\n", *buf);
        else    /* otherwise good value */
            printf ("  '%c' - accepted.\n", *buf);
        len = strlen (buf);    /* get length of input (+ '\n') */
        if (len > 2)    /* check if more than 1 character entered */
            puts ("  (additional characters entered)");
        /* validate entire line read */
        while (len && buf[len-1] != '\n') {
            fgets (buf, sizeof buf, stdin);
            len = strlen (buf);
        }
    }
    return 0;
}

Example Use/Output

$ ./bin/single123
press [Enter] alone or generate manual EOF to exit.

enter single char 1-3: 0
  error: invalid input '0'
enter single char 1-3: 1
  '1' - accepted.
enter single char 1-3: 2
  '2' - accepted.
enter single char 1-3: 3
  '3' - accepted.
enter single char 1-3: 4
  error: invalid input '4'
enter single char 1-3: 100
  '1' - accepted.
  (additional characters entered)
enter single char 1-3: 20000
  '2' - accepted.
  (additional characters entered)
enter single char 1-3: 33333
  '3' - accepted.
  (additional characters entered)
enter single char 1-3: 4444444444444444444444444444444
  error: invalid input '4'
  (additional characters entered)
enter single char 1-3:

Using getchar()

getchar() (and scanf() for that matter) provide an equally easy way to do the same thing, the difference being you will be handling things a character at a time from your input buffer rather than buffering the first buffer size number of characters -- and you must remove any additional character -- including the '\n' generated by pressing Enter, yourself. The code is actually a few lines shorter, e.g.

#include <stdio.h>

int main (void) {

    puts ("press [Enter] alone or generate manual EOF to exit.\n");

    while (1) {
        int c;          /* note: c must be declared as 'int' */
        fputs ("enter single char 1-3: ", stdout);
        c = getchar();
        if (c == EOF) { /* manual EOF */
            fputs ("(user canceled input)\n", stderr);
            break;
        }
        if (c == '\n')  /* check for [Enter] alone */
            break;
        if (c < '1' || '3' < c)     /* if not 1-3, error */
            fprintf (stderr, "  error: invalid input '%c'\n", c);
        else    /* otherwise good value */
            printf ("  '%c' - accepted.\n", c);
        /* check if more than 1 character entered */
        if ((c = getchar()) != '\n') {
            puts ("  (additional characters entered)");
            /* remove additional characters */
            do {
                c = getchar();
            } while (c != '\n' && c != EOF);
        }
    }
    return 0;
}

(use/output is the same)

Look things over and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
0

Is there a way to limit the user input to a single char between a given range?

No.

The user can input whatever he likes, but when your program receives that input, it's your duty to treat it right.

For example, you could read the whole input line as a string, check its length, and if it's not the desired length (1 in your case), inform the user.

gsamaras
  • 71,951
  • 46
  • 188
  • 305