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));
Related
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;
}
}
}
I have a piece of code which just asks for user input and keeps prompting user for input if the user entered 0 or less.
float n;
do {
printf("Enter a number: ");
scanf_s("%f", &n);
} while (n <= 0);
but when the user enters a Char it keeps executing printf() forever without prompting or waiting for user inputs.
I've used visual studio debuger with breakpoints and found that scanf() is executed without the user pressing any keys so i have no idea what's going on here.
As you may or may not know, scanf_s() returns the number of items that were successfuly converted and assigned to variables. However, when no items are converted and assigned successfully, it returns 0.
In your case, scanf_s() expects a float. So, when a char is entered instead, it doesn't match the conversion specifier and fails. As a result, scanf_s() returns 0.
In addition, the character isn't consumed and remains in the input buffer. So scanf_s() will continue to fail on that same character and loop infinitely.
Solution
1) Check the return value of scanf_s() for a failure.
2) Consume any unread characters when there's a failure.
Suggestions
1) Initialize your variable n, since it will be evaluated even when scanf_s() fails.
2) Since you want the user to enter a number greater than 0, state it specifically when prompting for one.
Code
float n = 0; /* initialize the variable */
do {
printf("Enter a number greater than 0: ");
if (scanf_s("%f", &n, 1) != 1)
while (getchar() != '\n'); /* consume any unread characters */
} while (n <= 0);
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);
}
}
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.
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;
}