Why does scanf enter a loop when I input a character? - c

I have to finnish a college project, and a part of my code is acting strangely.
The goal of that part is to get an user input of an integer and store it in a variable so that i can use it later, however if the user inputs a character I have to ask for the number again.
I used the scanf function to get the user input and put it inside a while loop to continuously ask for the input in case it's invalid.
The problem is that when a user inputs a character, the code freaks out and starts running the while loop without stopping in the scanf to get the user input.
It makes sense that the loop condition is always true but the strange part is that it doesn't stop to read new inputs.
I deconstructed my code in order to replicate the problem to make it easier to debug.
I know that there are some useless variables but in my original code they are useful, I just kept them there to make it look similar to the original.
I can only use scanf to get user input, despite knowing them, in this project I am only allowed to use scanf. I can't use scanf's format to get characters, only numerical types are allowed in this project.
C11 is the version of the standart we are using in classes.
I'm sory if the solution for this is a dumb thing, I'm not good at C and I'm having some difficultlies this semester...
Thanks in advance.
while (!verification) {
printf(">>>"); //write values in here
check = scanf("\n%d", &var); //input a number and store the number of valid inputs
if (check) verification = 1; //if the input is a number then the while condition should turn to false with this statement
printf("var = %d, check = %d, verification = %d\n", var, check, verification); //printing all variables
}

If the user does not input an integer there are characters left in the input stream after the call to scanf. Therefor you need to read to end of line before making the next attempt to read an integer. Otherwise scanf will try to read the same non-integer characters again and again. Here is an example:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int ch, i, n;
n = scanf("%d", &i);
while (n == 0) {
fprintf(stderr, "wrong input, integer expected\n");
do {
ch = getchar();
} while ((ch != EOF) && (ch != '\n'));
n = scanf("%d", &i);
}
if (n == 1) {
printf("%d\n", i);
} else { /*n == EOF*/
fprintf(stderr, "reading input failed\n");
exit(EXIT_FAILURE);
}
return 0;
}

Don't use scanf() to read input from the user.
It's really only meant for reading data that's known to be in a particular format, and input from a user... often isn't.
While you do correctly check the return value of scanf("%d"), and could fix the case where the input isn't a number, you'll still have problems if the input is either an empty line, or a number followed by something else (123 foobar).
In the case of an empty line scanf() will continue waiting for non-whitespace characters. This is probably confusing, since users will expect hitting enter to do something.
In the case there's trailing stuff after the number, that stuff stays in the input buffer, and the next time you read something, it gets read. This is again probably confusing, since users seldom expect their input to one question to also act as input to another.
Instead, read a full line with fgets() or getline(), then run sscanf() or strtol() on that. This is much more intuitive, and avoids the disconnect caused by scanf() consuming input lines only partially (or consuming more than one line). See also e.g. scanf() leaves the new line char in the buffer
Here, using getline() (POSIX, even if not in standard C. Use fgets() instead if getline() is not available):
#include <stdio.h>
int main(void)
{
char *line = NULL;
size_t len = 0;
int result;
printf("Please enter a number: ");
while (1) {
if (getline(&line, &len, stdin) == -1) {
/* eof or error, do whatever is sensible in your case */
return 1;
}
if (sscanf(line, "%d", &result) != 1) {
printf("That didn't seem like number, please try again: ");
continue;
}
break;
}
printf("You entered the number %d\n", result);
}

The problem is you must discard offending input when the conversion fails.
Here is a simple solution using only scanf() as instructed:
#include <stdio.h>
int main() {
int n;
for (;;) {
printf("Enter an number: ");
switch (scanf("%d", &n)) {
case 1:
/* successful conversion */
printf("The number is %d\n", n);
return 0;
case 0:
/* conversion failure: discard the rest of the line */
scanf("*[^\n]"); // discard characters before the newline if any
scanf("*1[\n]"); // optional: discard the newline if present
printf("Invalid input. Try again\n");
continue;
case EOF:
/* input failure */
printf("Premature end of file\n");
return 1;
}
}
}

Related

Validate integer against chars

I'm trying to make a program where the user inputs value to an array. What is actually required is that the program should validate against a char character. So if the user inputs a random char such as 'n' the program should tell him "You introduced a char, please input an integer: ".
How is that possible to make that without using a char variable?
for (i = 1; i <= size; i++) {
printf("Introduce the value #%d of the list: ", i);
scanf("%d", &list[i]);
if () { // I'm blocked right in this line of code.
printf("What you tried to introduce is a char, please input an integer: ");
scanf("%d", &list[i]);
}
Thanks in advance.
As #MFisherKDX says, check the return value of scanf. From the scanf man page:
These functions return the number of input items successfully matched
and assigned, which can be fewer than provided for, or even zero in
the event of an early matching failure.
The value EOF is returned if the end of input is reached before either
the first successful conversion or a matching failure occurs. EOF is
also returned if a read error occurs, in which case the error
indicator for the stream (see ferror(3)) is set, and errno is set
indicate the error.
So capturing the return value of scanf in an int variable and then comparing that variable to 1 (in your case, because you are only attempting to read 1 item) should tell you if scanf successfully read an integer value.
However, there is a nasty pitfall when using scanf that you should be aware of. If you do type n at the prompt, scanf will fail and return 0, but it will also not consume the input you typed. Which means that the next time you call scanf, it will read the same input (the n character you typed), and fail again. And it will keep doing so no matter how many times you call scanf. It always amazes me that computer science educators continue to teach scanf to students, given not only this potential pitfall, but several other pitfalls as well. I wish I had a nickel for every hour that some CS student somewhere has spent struggling to get scanf to behave the way their intuition tells them it should. I'd be retired on my own private island by now. But I digress.
One way around this particular pitfall is to check if scanf failed, and if so, to purposely consume and discard all input from stdin up to and including the next newline character or EOF, whichever comes first.
First let's look at some unfixed code that causes an infinite loop if you enter a non-integer as input:
// Typing the letter 'n' and hitting <Enter> here causes an infinite loop:
int num, status;
while (1) {
printf("Enter a number: ");
status = scanf("%d", &num);
if (status == 1)
printf("OK\n");
else
printf("Invalid number\n");
}
The above code will (after you type n and hit <Enter>), will enter an infinite loop, and just start spewing "Invalid number" over and over. Again, this is because the n you entered never gets cleared out of the input buffer.
There are a few possible ways to get around this problem, but the consensus seems to be that the most portable and reliable way to do so is as follows:
// Fixed. No more infinite loop.
int num, status;
while (1) {
printf("Enter a number: ");
status = scanf("%d", &num);
if (status == 1)
printf("OK\n");
else {
printf("Invalid number\n");
// Consume the bad input, so it doesn't keep getting re-read by scanf
int ch;
while ((ch = getchar()) != '\n' && ch != EOF) ;
if (ch == EOF) break;
}
}
The function scanf() will returns the number of elements read, so in this case it will return 1 every time it reads an int and 0 when it reads a char, so you just need to verify that return value.
Keep in mind that after reading a character it will remain in the buffer so if you use the scanf() command again it will read the character again and repeat the error. To avoid that you need to consume the character with while(getchar() != '\n');
With that in mind I modified your code so that it works properly printing an error message if a character is introduced and asking for a new int.
for (int i = 1; i <= size; i++) {
printf("Introduce the value #%d of the list: ", i);
while (!scanf("%d", &list[i])) { //verifies the return of scanf
while(getchar() != '\n'); //consumes the character in case of error
printf("What you tried to introduce is a char\n");
printf("please introduce the value #%d of the list: ", i);
}
}

scanf test failing inside a function in C

I'm trying to do a program with a simple game for a user to guess the number. My code is below:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX 30
#define TRYING 5
void guessnumber(int, int, int *);
int main(void) {
int mytry = 1;
guessnumber(MAX, TRYING, &mytry);
if (mytry <= TRYING)
printf("Congratulations! You got it right in %d tries\n", mytry);
else
printf("Unfortunately you could not guess the number in the number of tries predefined\n");
printf("End\n");
return EXIT_SUCCESS;
}
void guessnumber(int _n, int _m, int *_mytry) {
srandom(time(NULL));
int generated = 0, mynum = 0, test = 0;
generated = rand() % (_n + 1);
printf("Welcome to \"Guess the number\" \n");
printf("A number between 0 and %d was generated\n", _n);
printf("Guess the number:\n");
while (*_mytry <= TRYING) {
test = scanf(" %d", &mynum);
if (test != 1 || mynum < 0 || mynum > MAX)
printf("ERROR: please enter a valid number \n");
else
if (mynum > generated)
printf("Wrong! The number your trying to guess is smaller\n");
else
if (mynum < generated)
printf("Wrong ! The number your trying to guess is bigger\n");
else
break;
*_mytry = *_mytry + 1;
}
}
Okay, now the program is working pretty ok except for one thing: the scanf test.
It works if I try to enter a number out of my range (negative or above my upper limit) but it fails if I for example try to enter a letter. What it does is that it prints the message of error _m times and then it prints "Unfortunately you could not guess the number in the number of tries predefined" and "End".
What am I doing wrong and how can I fix this?
In case, a character is entered, you're trying to detect it correctly
if(test!=1 ......
but you took no action to correct it.
To elaborate, once a character is inputted, it causes a matching failure. So the input is not consumed and the loop falls back to the genesis position, only the loop counter is increased. Now, the previous input being unconsumed, is fed again to the scanf() causing failure once again.
This way, the loop continues, until the loop condition is false. Also, for every hit to scanf(), as unconsumed data is already present in the input buffer, no new prompt is given.
Solution: You need to clean the input buffer of existing contents when you face a failure. You can do something like
while ((c = getchar()) != '\n' && c != EOF);
to clean the buffer off existing contents.
When you enter a letter, scanf() leaves the letter in the input stream since it does not match the %d conversion specifier. The simplest thing to do is use getchar() to remove the unwanted character:
if (test != 1) {
getchar();
}
A better solution would be to use fgets() to get a line of input, and sscanf() to parse the input:
char buffer[100];
while (*_mytry<=TRYING)
{
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
fprintf(stderr, "Error in fgets()");
exit(EXIT_FAILURE);
}
test=sscanf(buffer, "%d", &mynum);
if(test!=1 || mynum<0 || mynum>MAX)
printf ("ERROR: please enter a valid number \n");
else if(mynum>generated)
printf("Wrong! The number your trying to guess is smaller\n");
else if(mynum<generated)
printf("Wrong ! The number your trying to guess is bigger\n");
else
break;
*_mytry=*_mytry+1;
}
In the above code, note that the leading space has been removed from the format string. A leading space in a format string causes scanf() to skip leading whitespaces, including newlines. This is useful when the first conversion specifier is %c, for example, because any previous input may have left a newline behind. But, the %d conversion specifier (and most other conversion specifiers) already skips leading whitespace, so it is not needed here.
Additionally, your code has srandom() instead of srand(); and the call to srand() should be made only once, and probably should be at the beginning of main(). And, identifiers with leading underscores are reserved in C, so you should change the names _m, _n, and _mytry.

Scanf and a manual function to get string when they work together

I have a trouble with scanf and a manual function to get string in the input.
Here is my manual function to get a line of string in input (I also get the [nl] character):
void getln(char *a) {
int i,c;
i=0;
do {
c=getchar();
a[i]=(char)c;
i++;
} while(c!='\n');
}
Then, I using it like this (char hs.school[40]; char hs.pc[20]; int hs.age;):
printf("Import age: ");
scanf("%d",&hs.age);
printf("Import personal code: ");
getln(hs.pc);
printf("Import school: ");
getln(hs.school);
The output:
Import age: 18
Import personal code: Import school: Vo Thi Sau
Why the getln call right after scanf call is ignored? (But the next getln works well)
Can you explain me the details and suggest me how to fix this bug. Thanks!
Edited:
Here is my full code that take the user inputs and export that inputs back to the screen, which is run well after I did a little trick, but I decide to make a question, mainly for expanding my knowlegde ^_^ Thanks for your answers.
#include<stdio.h>
void getln(char *);
void putstr(char *);
int main(void) {
struct Student {
struct Fullname {
char first[10],middle[20],last[10];
}fu;
struct Native {
char social[30],district[30],province[30];
}na;
struct Score {
double maths,physics,chemistry;
}sc;
char pc[20],school[40];
int age;
}hs;
printf("Import stage:\n");
printf("- Import full name:\n");
printf("++ First name: ");
getln(hs.fu.first);
printf("++ Middle name: ");
getln(hs.fu.middle);
printf("++ Last name: ");
getln(hs.fu.last);
printf("- Import native living place:\n");
printf("++ Social: ");
getln(hs.na.social);
printf("++ District: ");
getln(hs.na.district);
printf("++ Province: ");
getln(hs.na.province);
printf("- Import school: ");
getln(hs.school);
printf("- Import personal code: "); // I have done a little trick
getln(hs.pc); // before I post the question,
printf("- Import age: "); // which swaped these two stage,
scanf("%d",&hs.age); // but it's works like a charm ^_^
printf("- Import scores:\n");
printf("++ Mathematics: ");
scanf("%lf",&hs.sc.maths);
printf("++ Physics: ");
scanf("%lf",&hs.sc.physics);
printf("++ Chemistry: ");
scanf("%lf",&hs.sc.chemistry);
printf("\nExport stage:\n");
printf("- Full name: ");
putstr(hs.fu.first);
printf(" ");
putstr(hs.fu.middle);
printf(" ");
putstr(hs.fu.last);
printf(".\n");
printf("- Native living place: ");
putstr(hs.na.social);
printf(", ");
putstr(hs.na.district);
printf(", ");
putstr(hs.na.province);
printf(".\n");
printf("- School: ");
putstr(hs.school);
printf(".\n");
printf("- Personal code: ");
putstr(hs.pc);
printf(".\n");
printf("- Age: %d.\n",hs.age);
printf("- Scores (Mathematics, Physics, Chemistry): %.2lf, %.2lf, %.2lf.\n",hs.sc.maths,hs.sc.physics,hs.sc.chemistry);
return 0;
}
void getln(char *a) {
int i,c;
i=0;
do {
c=getchar();
a[i]=(char)c;
i++;
} while(c!='\n');
}
void putstr(char *a) {
int i;
i=0;
while(a[i]!='\n') {
putchar(a[i]);
i++;
}
}
After taking input hs.age you pressed in Enter, which is a \n character. So your getln() is called but the loop is broken just after one iteration as c contains '\n'. if you print hs.pc, there will be a new line in your output screen.
You are not clearing the input buffer. So in this newline will be placed after the first input given to scanf. So getchar will get the new line as a input. So loop will quit.
Use this line after the scanf.
int c;
if ( scanf("%d",&hs.age) != 1 ) {
printf("Invalid Input\n");retrun 0; }
while((c=getchar()) != '\n' && c != EOF );
It will clear the input buffer. Then it will ask the second input from the user.
Your getln call isn't skipped, it is taking the newline character left in stdin (the input buffer) as its input and it reads '\n' as c, assigns it to a[i], checks whether c is a '\n' char and exits.
To solve the initial problem, you need to clear the input buffer before calling getln. You can either do that with the while loop as suggested in the other answer, or you can craft a proper format string for scanf that will consume the newline, emptying the buffer. (not foolproof), but an alternative scanf would be:
scanf(" %d%*c",&hs.age);
Which would skip all whitespace before the number (including any newlines), read the decimal value, and then read and discard the newline. Note: this only works for a number without trailing characters. Entering 13abc would leave bc\n in stdin. The while loop in this case is more flexible as it reads all characters until a newline is encountered and is probably the better choice:
scanf(" %d",&hs.age);
while ((c = getchar()) != '\n' && c != EOF);
As for your getln function, it only needs to read each character into a[i]. There isn't a real need for c. You will also want the same checks on your input so that no newline is left. You will also want to check i against the maximum lengths of a minus 1. I would suggest a #define MAXS 128 for the maximum string length for your input. That would allow something to test i against to prevent writing beyond the end of your string.
Here is an alternative to your getln. Note: it is type int allowing it to return the length of the line read so you can determine what to do if it has reached MAXS (as there will still be characters in stdin at that point). As a general rule, if you are doing something in a function when there is a potential for error, it is better to return a value indicating success/failure/problem:
#define MAXS 128
...
int getln (char *a)
{
size_t i = 0;
while ((a[i] = getchar()) != '\n' && a[i] != EOF)
{
i++;
if (i == MAXS - 1)
{
a[i] = 0;
break;
}
}
return i;
}
Can you explain me the details ...
I'll try not to use confusing terms such as buffer.
You probably already know that "%d" corresponds to a set of decimal digit characters which get transformed into an int. When you press 'Enter' as others have suggested, the '\n' character is transmitted via stdin. '\n' isn't a decimal digit character, so it gets placed back onto the stream for your getln function to discover later on...
In reality, your "getln call right after scanf call" probably isn't ignored; it's probably just reading the trailing '\n' and seeing an empty line.
That is assuming the other problem isn't coming into play. getln can't see how many bytes a points to, so it can't tell when it's about to overflow, and hence makes no attempt to prevent buffer overflows... You've basically rewritten gets. If your input is lengthy enough, then I suppose this could also cause your problem... A buffer overflow is undefined behaviour, and the consequences of using undefined behaviour are undefined.
On the topic of undefined behaviour, since getln isn't technically producing a string, I do hope you're not using it as input for a standard string function later on...
Also on the topic of undefined behaviour, what do you suppose might happen if the user enters something that isn't a set of decimal digits? scanf conveys input errors via the return value... so never ignore the return value. You can (and should, at some point) find more information about this in the scanf manual.
... and suggest me how to fix this bug.
It doesn't make a whole lot of sense to discard user input, but unfortunately you can't expect a solution that results in better user experience without blowing your code size (and this explanation) well out of proportion.
You can discard the remainder of the line (which is probably just a '\n') following the set of decimal digits using scanf like so: scanf("%*[^\n]"); getchar();... Following the "%d" scanf call, of course... You could even merge the two together, like so:
if (scanf("%d%*[^\n]", &hs.age) != 1) {
puts("ERROR: EOF or file access error.");
exit(0);
}
getchar();
Unfortunately, if your user uses the spacebar key rather than enter, he or she probably won't find out about the problems with this until it's slightly too late...
As for the buffer overflow problem, I recommend using fgets rather than gets. fgets also has failure modes, which are conveyed via the return value and the contents of the array. The return value is used to convey EOF and file access errors, and the presense (or rather lack of) of a '\n' in the return value is used to convey when the input line was too large to store in the array. We can notify the user of the overflow (which I'm sure they'll appreciate) and discard the excess using the same scanf trick used earlier...
if (fgets(hs.pc, sizeof hs.pc, stdin) == NULL) {
puts("ERROR: EOF or file access error.");
exit(0);
}
size_t size = strcspn(hs.pc, "\n");
if (hs.pc[size] != '\n') {
printf("WARNING: MAXIMUM SIZE OF %zu EXCEEDED! LINE TRUNCATED.\n", sizeof hs.pc - 1);
scanf("%*[^\n]");
getchar();
}
hs.pc[size] = '\0';
I suppose it would make sense to wrap these solutions into functions, except that the functions would then promote the discarding of user input. Nonetheless, the later one is lengthy enough that you'd most likely benefit from abstraction...
void getln(char *a, size_t a_size) {
if (fgets(a, a_size, stdin) == NULL) {
puts("ERROR: EOF or file access error.");
exit(0);
}
size_t size = strcspn(a, "\n");
if (a[size] != '\n') {
printf("WARNING: MAXIMUM SIZE OF %zu EXCEEDED! LINE TRUNCATED.\n", a_size - 1);
scanf("%*[^\n]");
getchar();
}
a[size] = '\0';
}
... and now you can use that like so: getln(hs.pc, sizeof hs.pc);

How to determine invalid inputs in c

I want to take an float input but if the user gives a character input it will show invalid input, I didn't found a specific answer on the net.
How is it done?
While scanf is safe to parse a double, many compilers have deprecated its use (for good reason) becuase it is unsafe when parsing a string. Additionally, should the parse fail, you will be left with the remains in the input buffer and you will have to flush it yourself.
For these reasons, prefer a function like fgets, which checks the length of its supplied buffer, and then a function like strtod or sscanf to make the conversion.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char buf[64];
/* read */
if (fgets(buf, sizeof buf, stdin)) {
/* convert */
char *err;
double d = strtod(buf, &err);
if (*err) {
puts("entry invalid");
} else {
printf("you entered %lf", d);
}
}
return 0;
}
Consider the following code fragment:
int num;
float val;
num = scanf("%f", &val);
if(num != 1)
{
printf("You didn't enter a float!\n");
}
scanf returns the number of items that it successfully scans. In your case, you're trying to scan one item, a float. If the user doesn't enter a float, scanf will return 0.
Note: If scanf fails, whatever garbage data the user entered will still be in the stdin stream. You'll have to manually flush it. Here's one such way:
while(((ch = getchar()) != '\n') && (ch != EOF))
continue;
Warning: Don't use fflush on stdin.
Edit
Even if scanf doesn't fail, you may still have garbage in the stdin buffer. For example, if the user enters:
123xyz
123 will be assigned to the float, and everything after x will stay in the stdin stream.

Infinite loop code in C

In the below program I try to input a number between 1 to 100 but if I enter a 'character' or "string" ( like s or sova ) during the execution time of scanf() statement it creates a infinite loop. so I try to do .... when I input a string or a character it shown me a message like "wrong value entered. enter again" and it will enter again...
Thanx;
#include<stdio.h>
int main()
{
int a;
scanf("%d",&a);
while(!(a>=1&&a<=100))
{
printf("wrong value entered. enter again\n");
scanf("%d",&a);
}
printf("you enter %d. Thanxz",a);
return 0;
}
You need to check the return value of scanf
If the user has not entered a integer, you need to eat the input. The scanf function will continually say not a integer, try again. So if scanf returns 0 you need to deal with it
When you use scanf you are working with buffered input, this means that when you enter a value say "123" and press ENTER then "123" plus the ending character (ENTER) will all be added to the buffer. scanf then removes 123 since %d specifies that an integer should be read but if a user enters something invalid like a string instead then the buffer will not be emptied.
A better way to read input from the keyboard is to use fgets() where you specify a max length to read. Once you have the input you can use sscanf() to retrieve the numeric value from it. The ENTER till then not irritate your input.
char buffer[128];
fgets( buffer, 128, stdin );
sscanf( buffer, "%d", &a );
Also always check return values from functions as a rule of thumb so that you can do appropriate action if the function fails.
If the return value from scanf is not equal to the number of item you like the user to input, read all characters of the input buffer until there is a '\n'. But instead of copying a whole loop over and over again to the places in your code where the user should input something, you could wrap the loop in a function like this:
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
void input(const char *format,...)
{
va_list ap;
int r;
/* number of items [to read] */
int noi=0;
for(r=0;r<strlen(format)-1;r++)
{
if(format[r]=='%')
{
if(format[r+1]!='%')
noi++;
else
r++;
}
}
do
{
va_start(ap,format);
r=vscanf(format,ap);
va_end(ap);
if(r!=noi)
{
switch(r)
{
case EOF:
case 0:
printf("All wrong, try again!\n");
break;
default:
printf("Unexpected value after item no %d!\n",r);
}
while(getc(stdin)!='\n');
}
else
break;
} while(1);
}
Hope that helps,
Jan
Try this.
#include <stdio.h>
#define FLUSH while (getchar() != '\n') // macro to eat invalid input
int main (void) {
int a = 0;
printf ("Enter an integer: ");
scanf("%d", &a);
while (a < 1 || a > 100) {
FLUSH;
printf("Invalid input. Please try again: ");
scanf("%d",&a);
}
printf("You entered %d.\nThanks!\n", a);
return 0;
}
Your code shows several coding habits that need to be changed:
Include (void) in the parameter list of main().
Leave spaces on either side of binary operators: while(!(a>=1&&a<=100)) is needlessly ugly and hard to read.
Simplify your logical expressions. Why use (! (a>=1 && a<=100)) when it means the same thing as (a < 1 || a > 100), and the latter is so much easier to read?
Prompt for user input when needed. Don't have the cursor just sit at a blank line with no indication to the user about what to do.
Use proper grammar and capitalization in your prompts. There's no reason to be lazy in your programming.

Resources