I am new to C, but I know C#. In C#, I could use TryParse to ensure that the user typed in the correct datatype.
Here is the basic code I have for C:
int shallowDepth;
do
{
printf ("\nEnter a depth for the shallow end between 2-5 feet: ");
scanf ("%d", &shallowDepth);
if (shallowDepth < 2 || shallowDepth > 5)
{
printf("\nThe depth of the shallow end must be between 2-5 feet.");
}
}
while (shallowDepth < 2 || shallowDepth > 5);
The problem is if I type characters, such as "asdf", the program goes crazy and repeatedly says "Enter a depth for the shallow end between 2-5 feet: ". I'm not sure why this is exactly happening, but it has to be because it expects an int and I'm passing characters.
So how do I verify that the user inputted data is of int type before trying to store it in a variable? Thanks.
This is happening because with %d scanf will refuse to touch anything that does not look like a number and leaves the text in the buffer. The next time around it will again reach the same text and so on.
I recommend that you ditch scanf for now and try something like fgets and then one of the functions in the strtoXXX family such as strtoul or strtoumax. These functions have a well-defined way of reporting errors and you can easily prompt the user for more text.
For example you could do:
char str[LENGTH];
long x;
if (!fgets(str, sizeof str, stdin)) {
/* Early EOF or error. Either way, bail. */
}
x = strtol(line, NULL, 10);
At this point you could use your number, but be aware that:
You can specify a pointer for strtol to fill and it will point to the first unacceptable character
If the result cannot be represented as a long then strtol will set errno = ERANGE. If you plan to test for this you must set errno = 0 before the strtol
If you want to use scanf you can't test it before. But you don't need too!
In your code if the user doesn't enter a number (or something that starts with a number), scanf returns 0, because it returns the number of parameters it could read.
So you need to check the return value of scanf to check if anything could be read.
Second, you need to remove everything that's still in the puffer.
You can use something like this for that:
while(getchar()!='\n');
If you want to handle files as well, you should catch EOF there, too.
int shallowDepth;
int invalid;
do {
int stat;
invalid = 0;
printf ("\nEnter a depth for the shallow end between 2-5 feet: ");
stat = scanf ("%d", &shallowDepth);
if(stat != 1){
invalid = 1;
while(getchar() != '\n');//clear stdin
} else if (shallowDepth < 2 || shallowDepth > 5){
invalid = 1;
printf("\nThe depth of the shallow end must be between 2-5 feet.");
}
}while (invalid);
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'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 have this code, that prompts the user to enter a 2D vector, and I want to build in error protection so if a character, symbol, etc is entered instead of a character, it prints an error and prompts again. Assuming that this code works perfectly:
vect getVector(void)
{
vect temp;
bool flag = false; /* For repetition we use an
* input flag initialized to false */
while (flag == false)
{
printf("Enter a vector in the format x y: ");
if((scanf("%f%f", &temp.x, &temp.y)) != 2)
printf(" >Input Failure!\n");
else
flag = true; /* Input OK */
clearBuffer(); /* A function I defined elsewhere */
}
return temp;
}
Entering something like "1.w 2.5" or "a.c 3.2" works fine with the error protection. However, entering "1.2 3.c" or "1.6 2.54p" (anything ending with a character) it simply ignores and discards that character.
"1.2 3.c" reads "1.2 3.0", "1.6 2.54p" reads "1.6, 2.54" and so on.
How can I build in protection to detect this trailing character and print the error message? Hopefully I cana build something easily into what I've already got.
I've been suggested to store the input in a character string and test for the ASCII codes, and if they're in the range of numbers, then convert them back to float, but I have no clue how that could be done. Moreover I would like to know WHY the code ignores that trailing character, maybe I can then think of a solution myself. Maybe try detecting the character initially? Sorry if it's a silly question, I just can't wrap my head around it.
How can I read a character that is mistakenly typed at the end of an expecting floating point number?
I want to build in error protection
Time to stop using scanf() - everywhere. Yes it is possible to use scanf() and have robust error detection, but it is not productive.
Instead consider fgets() to read a line of user input #Felix Palmen and then parse the string.
Could use sscanf() and " %n" to detect extra garbage as below or use calls to strtof().
// return 1 on success
// return EOF on end-of-file or input error
int getVector(vect *v) {
char buf[100]; // make this 2x expected max size
vect temp;
bool repeat = true;
while (repeat) {
printf("Enter a vector in the format x y: ");
fflush(stdout);
if (fgets(buf, sizeof buf, stdin) == NULL) {
return EOF;
}
int n = 0;
sscanf(buf, "%f%f %n", &temp.x, &temp.y, &n);
// If scanning was incomplete or did not end at the end of the string, then fail
if (n == 0 || buf[n]) {
printf(" >Input Failure!\n");
} else {
repeat = false;
}
}
*v = temp;
return 1;
}
You can if you want to do a bit more manual work.
The strtof() family of functions will give you a bit more error information, but they expect a string (char*) as input.
For that, you'll need read into a buffer first (scanf("%s", buffer) will work for this, but as noted, can be susceptible to buffer overrun errors, fgets() will protect against this), then read your two floats from that buffer with strtof() like this:
printf("Enter a vector in the format x y: ");
scanf("%s %s", buffer1, buffer2);
char *cursor = buffer1;
temp.x = strtof(cursor, *cursor);
if(cursor < (buffer1 + strlen(buffer1)))
printf(" >Input Failure!\n");
cursor = buffer1;
temp.y = strtof(cursor, *cursor);
if(cursor < (buffer2 + strlen(buffer2)))
printf(" >Input Failure!\n");
The scanf() call here is reading two whitespace (space, tab, newline, etc.) separated strings into buffer1 and buffer2. Given that this is scanf() you will need to ensure that there is enough space to read the two strings so that the computer doesn't just start writing too far.
The two calls to strtof() here each read leading whitespace followed by a valid floating point number and then advance cursor to the first character that is no longer a part of that floating point number. Therefore if it hits a character that is not part of a valid float, it won't reach the end of the string, meaning a bad input was received.
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.
I am making a typical guessing game to learn C for the first time in my life and I noticed a bug. If you enter an integer you will get Guess a higher value or Guess a lower value and that works just fine. But if you put in a string, it goes insane and prints out a lot of strings saying Guess a higher value.
What I am trying to do now is to make a check for if the user enters a string, it will say to the user something like Enter a number, not text. How do I do this?
Is there anything you see that I can improve in this code?
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main () {
int secret, answer;
srand((unsigned)time(NULL));
secret = rand() % 10 + 1;
do {
printf ("Guess a number between 1 and 10");
scanf ("%d",&answer);
if (secret<answer) puts ("Guess a higher value");
else if (secret>answer) puts ("Guess a lower value");
} while (secret!=answer);
puts ("Congratz!");
return 0;
}
You are ignoring the return value of scanf, which will tell you if the conversion went well or not. scanf will return the number of items assigned, so if it returns 1 you know that answer was assigned to a number. If it returns 0, you know that something went wrong.
What I am trying to do now is to make a check for if the user enters a
string, it will say to the user something like Enter a number, not
text. How do I do this?
scanf does formatted input. You may also want to check out advanced formatting specifiers in scanf -- such as ignoring parts of the input, specifying buffer size for reading in strings, specifying only a particular set of characters that you want to read etc. If you want to verify input, you are probably best of reading a line from the console using fgets and then parsing the line.
Is there anything you see that I can improve in this code?
You may want to improve your random number generation algorithm. Read the C FAQ.
Check the return value of scanf . It will return the number of items successfully matched and assigned. In this case if you enter a string then it will not be matched and assigned, but the string stays in the input buffer, and the scanf tries to read it in each iteration and fails, and thus the problem arises.
You may change the code to:
int main () {
int secret, answer;
srand((unsigned)time(NULL));
secret = rand() % 10 + 1;
do {
printf ("Guess a number between 1 and 10");
if (scanf ("%d",&answer) != 1)
{
printf ("\nPlease enter an integer\n\n");
scanf ("%*s");
continue;
}
if (secret<answer) puts ("Guess a higher value");
else if (secret>answer) puts ("Guess a lower value");
} while (secret!=answer);
puts ("Congratz!");
return 0;
}
Note that inside the if the line scanf ("%*s"); is done. In the %*s the * is the input supression indicator, this tells that a string (stands for %s) will be read but the * tells that although the string will be read from the input, it will be discarded. This is being done to simply discard the previously entered string which is not read by the scanf ("%d",&answer) . In case you do not discard the string in the buffer, it will remain, and each iteration the scanf ("%d",&answer) will fail to match any integer, as it will encounter the leftover string in the input buffer.
On the other hand you may read a string and convert the string to an integer.
Like as below:
int main () {
int secret, answer;
char buff[128];
srand((unsigned)time(NULL));
secret = rand() % 10 + 1;
do {
printf ("Guess a number between 1 and 10");
scanf ("%s",buff);
if ((answer = atoi (buff)) == 0)
{
printf ("\nPlease enter an integer\n\n");
continue;
}
if (secret<answer) puts ("Guess a higher value");
else if (secret>answer) puts ("Guess a lower value");
} while (secret!=answer);
puts ("Congratz!");
return 0;
}
atoi () will convert a string to an integer (in 10 base). If the characters which comprises the integer does not contain valid digits, it will return 0. Checking for it we can detect if the user entered correctly. Also because your application needs to enter an integer within 1 and 10 therefore 0 is not included, therefore it is okay to take 0 as an invalid. To get better control on detecting an invalid integer format, and the location of the error in the string use strtol ()
You may want to make a function to check if the input is numeric. Like:
int isNumeric( char *str){
while(*str)
{
if (!isdigit(*str))
return 0;
str++;
}
return 1;
}
int main(){
char guess[3];
int iGuess;
do {
printf("Guess a number between 1 and 10");
gets(guess);
if (!isNumeric(guess)){
printf("Invalid input");
continue;
}
iGuess = atoi(guess);
if (iGuess<secret)
printf("Higher");
else if (iGuess>secret)
printf("Lower");
} while (secret!=iGuess);
printf("Congrats");
return 0;
}
You need to include ctype.h and string.h