-2

I'm learning C and I face a problem running this loop. I've wrote a while loop to prompt the user to key in the package and the quantity of it. I try to validate the input for the quantity to check is it integer or not (when a user key in a character it will prompt the user to key in again)

For the first run, everything is fine.

But when the loop runs a second time and so on, I try to key in a character for the quantity of the package, the message won't pop up to tell the user to key in again.

The value of the scanf is ignored and the value of tempQtty is equal to the previous quantity that the user keyed in.

Is there any way to fix this, or is there another way to validate the user input is integer?

Sorry for my broken English :")[input, expected input and actual input][1]

while(skip != 'x')
        {
            printf("\n\n%27sPACKAGE A/B/C/D  ( x = skip ) : ", "");
            rewind(stdin);
            package = getchar();

            switch (package)
            {
            case'x':case'X': skip = tolower(package); break;
            case'A':case'a':case'B':case'b': case'C':case'c':case'D':case'd':
                printf("%27sQUANTITY%21s: ", "", "");
                rewind(stdin);
                scanf("%d", &tempQtty);   //here's the problem

                while (tempQtty < 0)
                {
                    printf("%27s(PLEASE KEY IN A CORRECT VALUE!)\n", "");
                    printf("%27sQUANTITY%21s: ", "", "");
                    rewind(stdin);
                    scanf("%d", &tempQtty);
                }
                switch (package)
                {
                case 'A':case 'a': qttyA = tempQtty; totalQttyA += tempQtty; break;
                case 'B':case 'b': qttyB = tempQtty; totalQttyB += tempQtty; break;
                case 'C':case 'c': qttyC = tempQtty; totalQttyC += tempQtty; break;
                case 'D':case 'd': qttyD = tempQtty; totalQttyD += tempQtty; break;
                    
                }
                break;
                default:
                    printf("%27s(NO SUCH PACKAGE! PLEASE KEY IN AGAIN!)\n", "");
            }
        }
printf("\nA = %d", qttyA);
printf("\nB = %d", qttyB);


  [1]: https://i.stack.imgur.com/hBD82.png
Steve Summit
  • 45,437
  • 7
  • 70
  • 103
DWei
  • 13
  • 3
  • 1
    Please give the exact input, expected result and actual result. If you mean that you enter a character where an integer is expected then that is not going to work. `scanf` will return a failure and the input data will not be consumed. You need to always check the return value of `scanf`. Also, `scanf` is not great if the input can be wrong because incorrect input does not get read. Instead use `fgets` with `sscanf`. – kaylum Aug 15 '21 at 10:58
  • What is `getchar` doing? – Lajos Arpad Aug 15 '21 at 11:15
  • @DWei This is a surprisingly hard problem, especially if you stick to using `scanf` for input. You must do two things: (1) Check the return value of scanf: if it does not return 1, that means it did not successfully read the value you asked it to. (2) If `scanf` fails — if it returns less than 1 — you must somehow discard the bad input. (More on that in the next comment.) – Steve Summit Aug 15 '21 at 15:29
  • @DWei So you want to try to "flush" bad or unread input after calling `scanf`. Many, many people before you have had this problem, and there are many, many answers on SO about it. Unfortunately in all those answers I'm not finding one good, "canonical" one to point you to. But if you search on "scanf flush input" you'll find answers — just ignore any that suggest using `fflush(stdin)`, which is wrong. (Also you can ignore all the ones that explain why `fflush(stdin)` is wrong, because you already know that. Look for the answers that explain what to use instead.) – Steve Summit Aug 15 '21 at 15:30
  • @DWei But I have an alternative, somewhat heretical suggestion for you. Although it's a noble goal to try to check that the user accidentally entered "bad" input, and to try to prompt the user to try again, it turns out that *`scanf` is basically useless for this purpose*. It is just too hard to write good code, using `scanf`, to get input from the user, check it for validity, and re-prompt the user if necessary. It is so hard that I believe it is not worth it. See [this answer](https://stackoverflow.com/questions/2979209/using-fflushstdin/58884121#58884121) for more details. – Steve Summit Aug 15 '21 at 15:35
  • @SteveSummit The reason of using scanf is because it is the only way that my teacher teach us to get an input. I know that scanf is not really good to use (People commented on my previous question scolding me for using scanf hahaha) so I have tried to use gets and getchar. Spent my whole day for this problem even it seems like doesnt have a solution, but at least i learn something today. BTW tqvm for helping me, really appreatiate it and sorry again for my broken English. :) – DWei Aug 15 '21 at 16:05
  • @DWei I try not to "scold" people for using `scanf`, because I know it's what everyone's taught at first. But it's so sad, because it's so hard to use, and it's so hard to get your program working that you get frustrated and imagine that C is a terrible, no-fun language, when really it's just `scanf` that is terrible. Anyway, good luck! – Steve Summit Aug 15 '21 at 16:10

2 Answers2

0

I try to key in a character for the quantity of the package, the message won't pop up to tell the user to key in again.

The value of the scanf is ignored and the value of tempQtty is equal to the previous quantity that the user keyed in.

Is there any way to fix this, or is there another way to validate the user input is integer?

There is no base for your assumption that tempQtty would receive a negative value if you entered z. In fact, the C standard mandates that tempQtty is not affected if the conversion fails. The way to fix this is to not ignore the scanf return value, which tells whether input was valid.

Armali
  • 18,255
  • 14
  • 57
  • 171
0

First of all, and as I mentioned in a comment, this is a surprisingly complicated problem, and you are not alone in facing it. It's not a problem with you, or with the C language; it's basically just a problem with the scanf function itself.

Partial answer:

(1) Everywhere you have things like

scanf("%d", &tempQtty);
while (tempQtty < 0)
    ...

that's wrong. If you ask for integer input using %d, and if the user types something non-numeric, what scanf does not do is fill in tempQtty as -1. What it does do is return a value saying it couldn't convert what you (the programmer) asked. So you want to change this to something more like

while (scanf("%d", &tempQtty) != 1)
    ...

(2) If the user does not type the integer you requested, and if scanf returns 0 to tell you so, there's a problem: the non-numeric input the user typed is probably still sitting on the input stream. It looks like you may have realized this, and that you're trying to get rid of the unread input by calling rewind(stdin). But that won't work; that's not the way to do it.

What you want to do is write a little "helper" function like this:

void flush_unread_input()
{
    int c;
    do {
        c = getchar();
    } while(c != EOF && c != '\n');
}

Then, wherever you've detected an error (that is, wherever scanf has returned something other than 1), instead of calling rewind(stdin), just call flush_unread_input().

A few more points:

  • You may have to experiment where to call flush_unread_input and where not to. You can't just blindly sprinkle it everywhere, because it basically reads and discards the rest of the line, but that it means it can also read and discard an entire line, which might sometimes be a line that you actually wanted.
  • There are many ways to write a flush_unread_input function. I've shown one way that should be easy to understand, but you'll often see something more compact like while((c = getchar()) != EOF && c != '\n');. (Also I haven't tested the version I've shown here.)
  • My answer might have suggested that scanf returns 1 if it succeeds and 0 if it fails, but it's more complicated than that. scanf actually returns the number of items successfully converted and stored, which might be more than 1 if you have a format specifier with multiple % signs in it.

There's much more to say on this topic, but I don't have time to write a longer answer this morning.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103