Reading until I manage to enter an integer - c

I'm pretty new in C, I used to work in Python, and I'm trying to see if something that I read is integer number. If not, to read until I manage to entry a number.
I did some research and I found out that the function scanf actually returns 1 if the read is done suitably, and 0 otherwise.
So, I have written this code, and I don't understand why this is an infinite loop, writing "Give an integer" on the console
#include <stdio.h>
int main() {
int a;
int b = 1;
do {
printf("Give an integer\n");
b = scanf("%d", &a);
} while (b == 0);
}

The problem with scanf() is that it stops reading when the first white space is encountered for most specifiers like "%d", so it's left in the input and that's why reading again would cause a problem if you don't discard such white space because it will then return immediately the next time you call it. There is a mandatory white space that is introduced when you press Enter or Return, the '\n' new line character (or line feed).
If you want to read an integer and make sure you did you can try like this
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int
main(void)
{
long int value; // You can adapt this to use `int'
// but be careful with overflow
char line[100];
while (fgets(line, sizeof(line), stdin) != NULL) {
char *endptr;
value = strtol(line, &endptr, 10);
if ((isspace(*endptr) != 0) && (endptr != line))
break;
}
fprintf(stdout, "%ld\n", value);
return 0;
}
read strtol()'s manual to understand this code
You could as suggested in the comments, remove the white space characters from the input, but IMHO that is harder and you would still have other problems with scanf(), like handing empty input which is not straight forward.

I don t understand why this is an infinite loop, writing "Give an intiger" on the console
The problem is that scanf() does not consume data that it cannot match against the specified format. It leaves such characters unread in the input stream. Therefore, if you try reading again from the same stream with the same format, without consuming at least one character by some other means, then you can be certain that the input will again not be matched. And again. And again.
To avoid your infinite loop, you need to consume at least one character of the non-matching input after each matching failure. There are many ways you could do that; here's a fairly simple one:
#include <stdio.h>
int main() {
int a;
do {
printf("Give an intiger\n");
if (scanf("%d", &a)) {
// breaks from the loop on a successful match or an error
break;
}
// consume the remainder of one line of input without storing it
if (scanf("%*[^\n]") == EOF) {
break;
}
} while (1);
}
That consumes the whole remainder of the line on which the non-matching input is encountered, which will yield less surprising interactive behavior for some inputs than many alternatives would.
If you've a penchant for writing terse code, or if you don't like to break out of the middle of a loop, then you might write the same thing like this:
#include <stdio.h>
int main() {
int a;
do {
printf("Give an intiger\n");
} while ((scanf("%d", &a) == 0) && (scanf("%*[^\n]") != EOF));
}
Because the && operator short circuits, the second scanf() call will be executed only if the first returns zero, and the loop will exit after the first iteration wherein either the first scanf() call returns nonzero or the second returns EOF (indicating an error).

Related

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

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;
}
}
}

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.

reading string with space

Say for example I have the following main function:
int main()
{
char a[1023];
while (scanf("%s",a) != EOF)
{
printf("%s ",a);
}
}
If I input
a ab abc(newline)
it should output:
a ab abc(newline)
but the main function output
a ab abc (newline) // there is a space between newline and the last string
I want to read the string one by one. What is the problem and how can I delete that space? Thank you.
Your printf("%s ",a); statement outputs a space after each string it outputs, regardless. You need to instead output a space only if there are going to be more strings. Equivalently (and easier), output a space before each string if it is not the first:
int first = 1;
while (scanf("%s",a) != EOF) {
if (!first) printf(" ");
printf("%s",a);
first = 0;
}
also, you never output a newline, despite describing a newline as being output...
A little note about independent learning:
The first time you use any standard library function, you should read its corresponding manual at least once. The reason is that as soon as you know about any issues (or helpful features), you'll know to avoid those issues (or use the helpful features). Reading the manual really helps you learn the full potential and avoid the pitfalls of C.
The first time you read something, you may not entirely understand it. Don't let that bother you; Just keep reading. If this happens, I suggest that you read it again once you're at the bottom of the page, and try to understand it completely the second time. Please, read the fscanf manual at least once.
That's out of the way, now. You might notice little ^ superscripts here and there. My intention is to annotate facts that you would have learnt by reading and understanding the manual I linked to above.
The format specifier %s discards as much whitespace as possible prior to attempting to consume as much non-whitespace as possible^. Suppose you were to input something with four leading bytes of whitespace, your output wouldn't have those four leading bytes of whitespace.
"This uses four spaces" translates to "This uses four spaces".
It seems logical that this violates your program specification, which extends your problem^. Perhaps you should be using the %[ format specifier, instead^. In that case, an empty line would be considered a match failure and the destination for the line would be indeterminate, so you'd need to handle the return value of scanf better^ (which I encourage you to do, anyway).
#include <stdio.h>
int main(void) {
char line[512];
int x;
do {
x = scanf("%511[^\n]", line);
if (x == 0) {
/* Match failure caused by inputting an empty line. Print empty line? */
putchar('\n');
}
else if (x == 1) {
/* Success. */
puts(line);
}
/* Read and discard the remainder of a line */
do { x = getchar(); } while (x >= 0 && x != '\n');
} while (x != EOF);
}
From the length of this loop and the simplicity of the problem, you might reason that using scanf for this is probably the wrong way to solve the problem. You can eliminate the line array, the calls to scanf and the resulting scanf error checking by using a derivation of the inner-most loop. Something like this looks nice:
#include <stdio.h>
int main(void) {
int c;
do {
c = getchar();
} while (c >= 0 && putchar(c) >= 0 && c != '\n');
}
PS. There's also a manual for getchar, putchar and many other standard things.

Why does scanf get stuck in an infinite loop on invalid input? [duplicate]

This question already has answers here:
Why is scanf() causing infinite loop in this code?
(16 answers)
Closed 9 years ago.
In line 5 I read an integer and isint is getting 1 if it reads an integer or 0 if it's not an integer. If isint is 0 I have a loop asking user to give an integer and I read until the user gives an integer. I try this code giving a character instead of an integer but I have an infinite loop. The program just doesn't wait to give a new input. What's wrong with my code?
#include <stdio.h>
int main(void) {
int arg1;
//int arg2;
int attacknum = 1;
int isint = 1;
//printf("Insert argument attacks and press 0 when you have done this.\n");
printf("Attack %d\n", attacknum);
attacknum++;
printf("Give attacking argument:");
isint = scanf("%d", &arg1); //line 5
while(isint == 0){
printf("You did not enter a number. Please enter an argument's number\n");
isint = scanf("%d", &arg1);
printf("is int is %d\n", isint);
}
return 0;
}
As others have mentioned, if scanf can't parse the input, it leaves it unscanned.
Generally scanf is a poor choice for interactive input because of this kind of behavior, and because it doesn't match the line-at-a-time interface experienced by the user.
You are better off reading one line into a buffer using fgets. Then parse that line using sscanf. If you don't like the input, throw the whole line away and read another one.
Something like this:
#include <stdio.h>
int main(void)
{
char line[256];
int arg1;
int isint;
while (1) {
printf("Give attacking argument:");
fgets(line, sizeof line, stdin);
isint = sscanf(line, "%d",&arg1);
if (isint) break;
printf("You did not enter a number.Please enter an argument's number\n");
}
printf("Thanks for entering %d\n", arg1);
return 0;
}
(For production code you'll want to handle long lines, check return codes, also check for trailing garbage after the number, etc.)
Actually, an even better approach would be to not use scanf if you just want to read an integer, and instead use strtol. That gives you a handy pointer to the character just after the number, and you can check that it's whitespace or nul.
When scanf is confronted with a non-digit it will not consume any input and return that zero integers were read. The non-digit will stay in the input for the next call to scanf that will behave the same as the first call, etc.
In answer to your question below. You could use fgetc to parse at least one character, but this will give the error messages for every character already typed. Typically I think you want to skip until a newline. To this end you could use fgets as suggested by poolie. Or you could add the following after scanf.
int ch;
if (isint == 0)
while ((ch = fgetc(stdin)) != EOF && ch != '\n')
{
/* Skip characters */
}
P.S: In your case it is probably better to put it just before the first printf in the loop.

scanf is skipping scans

I am writing a program in C with multiple scanfs, but when running it, whenever I get to a scan that is reading for an integer value, it skips it and puts in a different value that begins executing an endless loop. I have even tried separating each of the scanfs into multiple functions and the same thing happens. I have absolutly no idea what is wrong, or what to do for that matter.
Check the return value. C library functions return status codes for a reason.
The main problem people strike with scanf is that the data isn't what they expected. That can lead to a partial scan, leaving you at an unexpected point in the file for future scans. This may be what's causing your infinite loop and you would normally detect it by ensuring that the return value is what you expect (the number of items you tried to scan). It's a little hard to tell without seeing the code.
C99 states, in section 7.19.6.4 The scanf function:
The scanf function returns the value of the macro EOF if an input failure occurs before any conversion. Otherwise, the scanf function returns the number of input items assigned, which can be fewer than provided for, or even zero, in the event of an early matching failure.
But, almost invariably, input should be retrieved as lines and then processed from there with sscanf, since this allows an easy way to try multiple scans on the input data for different format strings, as many times as it takes to figure out what format the line is in.
For example, the following code is a safe way of retrieving user input with buffer overflow protection and detection, and buffer clearing so excess input doesn't affect the next input operation:
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
You can call this code with something like:
char buff[50];
int rc = getLine ("What?> ", buff, sizeof(buff));
if (rc != OK) {
// Error condition, either EOF or to long.
}
// Now use sscanf on buff to your heart's content,
// remembering to check the return value.
If you are reading from stdin, scanf will read from a buffer that ends when you press return.
The first scanf will take in what you're looking for, but the rest of the buffer will remain.
For code:
int num;
scanf("%d", &num);
and input:
1 2 3 4 5
num will be 5, as expected. But
2 3 4 5
will still be in the buffer, so the next scanf you run will not prompt for another input, but instead take that as the input.
Your scanf may be reading residual data from a previous buffer.

Resources