1

Heres a part of my code:

    printf("\nEnter amount of adult tickets:");
    scanf("%d", &TktAdult);
    while (TktAdult<0){
        printf("\nPlease enter a positive number!");
        printf("\nEnter amount of adult tickets:");
        scanf("%d", &TktAdult);
    }

Right now it can only stop user from entering a negative value, but how do I add to it so it also stops user from entering a char??

  • 1
    Read the data to a char* buffer, and parse it. – Bathsheba Dec 14 '16 at 15:11
  • have you tried with atoi? https://www.tutorialspoint.com/c_standard_library/c_function_atoi.htm – HRgiger Dec 14 '16 at 15:13
  • @HRgiger don't suggest atoi, it is old unsafe and hard to use. – cat Dec 14 '16 at 15:35
  • @HRgiger https://stackoverflow.com/questions/17710018/why-shouldnt-i-use-atoi – cat Dec 14 '16 at 15:36
  • Please see [ask] and provide a [mcve]. – too honest for this site Dec 14 '16 at 15:41
  • @Olaf what was wrong with my question sir? :/ – NotAProgrammer Dec 14 '16 at 15:54
  • a %d format specifier will not allow a character to be entered.. Note the logic of the posted code is rather convoluted. But most of all, always check the returned value (not the parameter value) of any call to any of the `scanf()` family of functions to assure the operation was successful. Suggest reading the `man` page for `scanf()` to get all the details, but of current interest is the `returned value` paragraph. – user3629249 Dec 15 '16 at 05:56

5 Answers5

5

... stop user from entering a negative value ...

is not possible. Users enter all sorts of gibberish. Instead, read a line of user input and parse it for correctness. Use fgets() for input, then sscanf(), strtol(), etc. for parsing.

// return -1 on EOF
int GetPositiveNumber(const char *prompt, const char *reprompt) {
  char buf[100];
  fputs(prompt, stdout);
  fflush(stdout);
  while (fgets(buf, sizeof buf, stdin)) [
    int value;
    if (sscanf(buf, "%d", &value) == 1 && value > 0) {
      return value;
    }
    fputs(reprompt, stdout);
    fflush(stdout);
  }
  return -1;
}

// Usage
int TktAdult = GetPositiveNumber(
    "\nEnter amount of adult tickets:" , 
    "\nPlease enter a positive number!");
if (TktAdult < 0) Handle_End_of_File();
else Success(TktAdult);
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • `getline()` is more useful than `fgets()`, since it can allocate the needed buffer. The problem however is that `fgets()` might be less portable. – glauxosdever Dec 14 '16 at 16:09
  • @glauxosdever `getline()` is not part of the standard C library. `fgets()` is part of the standard C library making `fgets()` _more portable_ than `getline()`. `getline()` has a weakness as it allows the hacker exploit of overwhelming computer resources based on user input. Numeric input exceeding 100 characters is certainly an error or attack. Code only needs to process the data in those cases, not allocate large memory. – chux - Reinstate Monica Dec 14 '16 at 16:45
  • Clearly a typo. I definitely meant `getline()` is less portable and not `fgets()`. – glauxosdever Dec 14 '16 at 16:56
  • 1
    To be fair, while I'm concerned much about security, I didn't ever think `getline()` could be exploitable this way. Good catch! – glauxosdever Dec 14 '16 at 16:58
  • @glauxosdever I like `getline()` over `fgets()` for _file_ input, not `stdin`, but even that is getting exploitable. If a `getline_s()` existed with an upper bound, that would be good, yet then that is similar to`fgets()` with a pre-allocated buffer. IMO, a robust user input function is not possible simple using current functions given todays nefarious users. Feel like using `fgetc()` is the only safe building block. Sigh. – chux - Reinstate Monica Dec 14 '16 at 17:07
3

but how do I add to it so it also stops user from entering a char??

You can't prevent user from entering character values. What you can do is check if tktAdult was successfully scanned. If not, flush out everything from stdin:

 bool valid = false;
 while (true) {
 ...

    if (scanf("%d", &TktAdult) != 1) {
       int c;
       while ((c = getchar()) != EOF && c != '\n');
    } else {
       break;
    }
}

The better alternative is to use fgets() and then parse the line using sscanf().

Also see: Why does everyone say not to use scanf? What should I use instead?.

P.P
  • 117,907
  • 20
  • 175
  • 238
  • I wouldn't suggest use of scanf either since it's hard to use and error-prone compared to `fgets` + `strtoull` – cat Dec 14 '16 at 15:37
  • @cat I suggested it too and the link explains it very well. `sscanf()` approach works just as well as `strtoull`. I suppose copy-pasteable code is valued more on SO. – P.P Dec 14 '16 at 16:10
  • I tried this... but it still accepts numeric input immediately followed by non-numeric characters as valid (i.e. 123sda is accepted as 123). Hence, I added a bit of additional code taking off from this one. – Leon Carlo Valencia Dec 14 '16 at 18:43
3
// You can try this,  Filter all except for the digital characters
int TktAdult;
char ch;
char str[10];
int i = 0;
printf("\nEnter amount of adult tickets:");
while ((ch = getchar()) != '\n')
{
    // Filter all except for the digital characters
    if(!isalpha(ch) && isalnum(ch))
        str[i++] = ch;
}
str[i] = '\0';
TktAdult = atoi(str);
printf("Done [%d]\n", TktAdult);
xw Lee
  • 39
  • 4
  • 1
    Better to use `int ch` than `char ch` to prevent UB in the `is...()` calls and to detect `EOF` (which causes an infinite loop here). Suggest adding a limit to the number of iterations in `while()` so `i` does not get too big. – chux - Reinstate Monica Dec 14 '16 at 16:49
  • 1
    Please, don't suggest atoi. https://stackoverflow.com/questions/17710018/why-shouldnt-i-use-atoi – cat Dec 14 '16 at 17:40
1

The following code rejects user input that are:

  • non-numeric as handled by // 1;

  • negative numbers as handled by // 2;

  • positive numbers followed by non-numeric chars as handled by //3

     while (1) {
         printf("\nEnter amount of adult tickets: ");
         if (scanf("%d", &TktAdult) < 0 ||  // 1
                 TktAdult < 0 ||  // 2
                 ((next = getchar()) != EOF && next != '\n')) {  // 3
             clearerr(stdin);
             do
                 next = getchar();
             while (next != EOF && next != '\n');  // 4
             clearerr(stdin);
             printf("\nPlease enter a positive number!");
         } else {
             break;
         }
     }
    

Also, // 4 clears the standard input of buffered non-numeric characters following case // 3 (i.e. 123sda - scanf takes 123 but leaves 'sda' in the buffer).

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
Leon Carlo Valencia
  • 7,979
  • 1
  • 11
  • 12
  • Except that scanf("%d", &TktAdult) returns a negative value and not a 0 for non-numeric input. Yup, next is declared on top of that while along with declaration of TktAdult. It's just a code snippet with the declarations taken out. Anyway, I've tested that code already for all 3 cases before posting it. So I'm pretty confident it works. – Leon Carlo Valencia Dec 15 '16 at 15:50
0
#include <stdio.h>

int main() {
    int x = 0.0;
    int readReasult;
    while(1)
    {
        readReasult = scanf("%d",&x);
        if (readReasult < 0) break;
        if (readReasult == 1) {
            if (x >= 0)
                printf("next number: %d\n", x);
            else
                printf("negative value not expected\n");
        } else {
            clearerr(stdin);
            scanf("%*s");
            printf("wrong input\n");
        }
    }

    return 0;
}

https://godbolt.org/z/xn86qz

Marek R
  • 32,568
  • 6
  • 55
  • 140