vC: scan numeric input until EOF - c

I have an assignment, when in C programming language, I have to scan multiple numeric (just integer, if it's not integer, it should exit) values from keyboard. Values are divided by space and input is ended by EOF. I need to further work with these scanned values. I know maximum of putted numbers, I don't know beforehand how many numbers I'll get.
I tried:
while (scanf("%d", &a) == 1 && count <= 10000 ) {
eof=a;
if ( eof=getchar() == EOF ) break;
...
But it doesn't seem to work as I need (often you have to give EOF twice, but not always, and it sometimes adds 0 to the input). Same happens when I use just:
while (scanf("%d", &a) == 1) {
if I try:
while (... && (a=getchar()) != EOF) {
the variable a is rewritten and I can't work with it further.
Thanks in advance.
EDIT: Furthermore, I need to distinguish between EOF and invalid input (something other than number), which I am not sure how to do, because
scanf(...)==1
won't do that job.

Note the result of scanf() be it 0, 1,or EOF.
int count = 0;
int a;
int scnt;
while ((scnt = scanf("%d", &a) != EOF && count < 10000 ) { // not <= 10000
if (scnt == 0) {
// Handle stdin with unconvertible input. OP wants "it should exit"
puts("Bad");
exit (-1);
} else {
// Success! Let's print it
printf("%d %d\n", count++, a);
}
}
// why did we quit?
if (count >= 10000)
puts("Too many");
else if (feof(stdin)) {
puts("EOF detected");
else if (ferror(stdin)) { // rare
puts("IO error on detected");
else // Should never occur
puts("???");

Instead of scanning for integers, read the whole line at once using e.g. fgets, and then get the number from that.
Then you can easily just put the fgets call in the loop condition, and don't need any other check.
If fgets is impractical to use, then how about nested loops, one outer that ends on end-of-file condition (a special flag you set), and an inner that uses e.g. getchar to read all characters. If getchar returns EOF you set the flag for the outer loop and break out of the inner loop.
Of course, this means you have parse and put together the numbers yourself, but unless you have to handle larger numbers that can be handled by the normal standard types then it's trivial.

Related

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.

C program loops infinitely after scanf gets unexpected data

I have a program where I want the input integer to be between 2 and 64 inclusive, so I put scanf inside a do { ... } while loop. Here's the code I initially tested:
int initialBase;
do {
printf("Initial base: ");
scanf("%i", &initialBase);
} while (initialBase < 2 || initialBase > 64);
The problem is whenever the input is not a valid integer, it just outputs the printf statement indefinitely and no longer prompts for user input, instantly flooding the console. Why is that happening and what's a better way of reading input that satisfies the conditions I want?
When scanf() fails, the argument is not automatically initialized, and uninitialized values could be any value, so it might be less than 2 or greater than 64 no one knows.
Try this
int initialBase;
/* some default value would be good. */
initialBase = 2;
do {
printf("Initial base: ");
if (scanf("%i", &initialBase) != 1)
break;
} while ((initialBase < 2) || (initialBase > 64));
the check will break out of the loop if you input something that is not a number, the initialiazation of initialBase is just a good habit which in your case could have prevented the behavior you describe, but in this case it's there to prevent accessing an uninitialized value after the while loop.
The reason the loop didn't stop, was because scanf() leaves some characters in the input stream when they are not matched, and calling scanf() again while those characters are still there will make scanf() keep waiting for valid input, but returning immediatly with the currently invalid input that is in the stream, if you want to keep reading, try reading characters from the stream until a '\n' is found, this way
int initialBase;
initialBase = 0;
do {
printf("Initial base: ");
if (scanf("%i", &initialBase) != 1)
{
while (fgetc(stdin) != '\n');
continue;
}
} while ((initialBase < 2) || (initialBase > 64));

How to check for end of input? C, find 42

This is the problem:Your program is to use the brute-force approach in order to find the Answer to Life, the Universe, and Everything. More precisely... rewrite small numbers from input to output. Stop processing input after reading in the number 42. All numbers at input are integers of one or two digits.
Input:
1
2
88
42
99
My first code doesn't work:
while( scanf("%d\n", &n) != 42 ){
printf("%d\n",n);
}
second code, with for loop, works but there is a test case where there is no number 42 so it returns TLE, how do I check for end of input?
for(i=1;i>0;i++){
scanf("%d\n",&n);
if(n!=42 ){ /* end of input??? */
printf("%d\n",n);
}
else {
break;
}
}
And why doesn't while loop work like it should?
scanf returns the number of characters scanned, not the result.
So, write while loop as follows:
scanf("%d", &n);
while( n != 42 ){
printf("%d\n",n);
scanf("%d", &n);
}
Always good to avoid magic numbers. Define a constant.
Check the result of scanf() (#Joachim Pileborg)
To check if the input is valid , test if scanf() result is 1 (1 format specifier correctly scanned).
To check for end of input, test if scanf() result is EOF.
while loop failure is well explained by #dbasic. Roughly, scanf() reports the number of fields scanned, not the value scanned.
There is a lot to scanf(). If up to it, read the scanf() section of the C spec. Where do I find the current C or C++ standard documents?
const int Answer_to_Life_the_Universe_and_Everything = 42;
int n;
int cnt;
// Use 2d to limit to 2 digits
while ((cnt = scanf("%2d\n",&n)) == 1) {
if(n != Answer_to_Life_the_Universe_and_Everything) { /* end of input? */
printf("%d\n",n);
}
else {
break;
}
}
if (cnt != EOF) Handle_UnexpectedInput(); // example someone type in "junk"

Validating Integer in C Programming

How can I make sure the number input by user is integer ranging from 0 to 4 only and also making sure that it is non negative/non symbol/alphabet??
If I do this,then it will not be able to validate alphabet/symbol
printf("Enter number 0 to 4");
scanf("%d",x);
if((x<0)||(x>4))
{
printf("0 to 4 only");
scanf("%d",x);
}
else
{
xxxxxx
}
First %d format from scanf expects a pointer to int. So you will write:
scanf("%d", &x);
Then you can test whether the read data match with the format, using scanf return value:
if (scanf("%d", &x) != 1 || x < 0 || x > 4)
{
/* Wrong input */
}
else
{
/* Well-formed input */
}
Read man scanf for further informations.
If the input should be a single number on the line, then:
char line[4096];
int x;
if (fgets(line, sizeof(line), stdin) == 0)
...EOF or other major trouble...
else if (sscanf(line, "%d", &x) != 1)
...not an integer...
else if (x < 0)
...negative - not allowed...
else if (x > 4)
...too large - maximum is 4...
else
...Hooray - number is valid in the range 0-4...use it...
You get to choose how the errors are handled. The first error handling should abandon the efforts to get a number from the user; the other three could warrant a retry (but keep an eye on how many retries you allow; if they get it wrong 10 times in a row, it is probably time to give up).
The key point is that the code uses fgets() to get the whole line, and then parses that. It would be possible to do further analysis — to make sure there isn't extra information on the line (so the user didn't type '3 dogs' instead of just '3'). This also allows you to report errors in terms of the whole line. The test for if (sscanf(line, "%d", &x) != 1) is also important. The members of the scanf()-family of functions report the number of successful conversion specifications (%d is a conversion specification). Here, if sscanf() successfully converts an integer, it will return 1; otherwise, it may return 0 or EOF (though EOF is unlikely with sscanf()). If there were 3 conversion specifications, the correct check would be != 3; it might report 1 or 2 successful conversions.

How to terminate a loop when a letter entered in C?

In my task I need to use a loop and get an input between 1-5, if i get any other input i need to keep iterating until i get 1-5.
Could you please tell me what am i doing wrong?
Part of my code:
int rateSelected, weeklyHours;
printf("Enter the number corresponding to the desired pay rate or action:\n");
printf("1) %.2lf$/hr 2) %.2lf$/hr\n", RATE1, RATE2);
printf("3) %.2lf$/hr 4) %.2lf$/hr\n", RATE3, RATE4);
printf("5) Quit\n");
while ((scanf("%d", &rateSelected)) != EOF && rateSelected != 5)
{
if (rateSelected > 5 || isalpha(rateSelected) ==1){
printf("please enter a number between 1-5:\n");
continue;
}
printf("Now enter your weekly hours:\n");
scanf("%d", &weeklyHours);
ChoosePayRate(rateSelected, weeklyHours);
}
tnx
The problem is your use of %d format specifier. When letters are entered instead of digits, scanf returns zero to indicate that nothing is read. If you would like to allow entering letters along with digits, you should either add a read of a string when scanf returns zero, or always read into a string buffer, and then use sscanf or atoi to convert the string to integer.
You better use fgets() and strtol() for this. Scanf and the line-buffering of stdio is not very helpful together...
char line[LINE_MAX];
do {
fgets(line, sizeof(line), stdin);
} while(!isdigit(line[0]));
int choice = strtol(line, NULL, 10);
isalpha(rateselected) will never be true because you are storing an int in rateselected.
scanf("%d",rateselected) allready takes care of catching character input, and returns 0 if that is the case. So you should change the isalpha test to a rateselected == 0 test.
Also, scanf will never return EOF. It will return 0, and then you need to test feof(stdin) to see if you really hit the end of input. (which would correspond to a ctrl-Z for keyboard input).
Remove the isalpha(rateSelected).
isalpha() checks if the value passed as parameter is an alphanumeric character - but you are passing the int value which you have just read.
However, this is still not sufficient - you would need to catch the return value from scanf() to check if scanf() has actually read an int. But if no int was entered, the characters are not discarded so that the next scanf() will again try to convert them, which leads to an endless loop.
Better use the solution provided by #dasblinkenlight.
Use this:
int e;
while ((e = scanf("%d", &rateSelected)) != EOF)
{
scanf("%*[^\n]"); // this clean your input buffer
if (e==0 || rateSelected>5 || rateSelected<1) {
printf("please enter a number between 1-5:\n");
continue;
}
instead of
while ((scanf("%d", &rateSelected)) != EOF && rateSelected != 5)
{
if (rateSelected > 5 || isalpha(rateSelected) ==1){
printf("please enter a number between 1-5:\n");
continue;
}

Resources