scanf not working on invalid input - c

On a character input in the first scanf(), the second one doesn't run. getchar() isn't working either for Try Again input. It skips to take input for Would you like to play again? (Y/N)? It seems that your_choice is supposed to take the character and check it afterward but the character is actually being taken by ch. What is causing it to work like this and how to resolve the issue. I've tried re-initializing the variables but doesn't work.
#include <stdio.h>
void choice(int);
int main() {
char ch;
int random, your_choice;
do {
srand(time(NULL));
system("cls");
printf("** 0 is for Rock **\n");
printf("** 1 is for Scissors **\n");
printf("** 2 is for Lizard **\n");
printf("** 3 is for Paper **\n");
printf("** 4 is for Spock **\n");
printf("\nEnter your choice here:");
scanf("%d", &your_choice);
random = rand() % 5; //random number between 0 & 4
if ((your_choice >= 0) && (your_choice <= 4)) {
//choice printer omitted for this post
if ((random == ((your_choice + 1) % 5)) || (random == ((your_choice + 2) % 5)))
printf("\n\n... and you win!!!\n");
else if ((random == ((your_choice + 3) % 5)) || (random == ((your_choice + 4) % 5)))
printf("\n\n... and you lose!!!\n");
else if (random == your_choice)
printf("\n\nUnfortunately, it's a tie!\n");
} else
printf("\nWell, this is wrong! Try again with a number from 0 to 4!!\n");
printf("\nWould you like to play again? (Y/N)?: ");
scanf(" %c", &ch);
} while (ch == 'y' || ch == 'Y');
return 0;
}

If the user enters characters that cannot be converted to a number, scanf("%d", &your_choice); returns 0 and your_choice is left unmodified, so it is uninitialized. The behavior is undefined.
You should test for this and skip the offending input this way:
if (scanf("%d", &your_choice) != 1) {
int c;
/* read and ignore the rest of the line */
while ((c = getchar()) != EOF && c != '\n')
continue;
if (c == EOF) {
/* premature end of file */
return 1;
}
your_choice = -1;
}
Explanation:
scanf() returns the number of successful conversions. If the user types a number, it is converted and stored into your_choice and scanf() returns 1, if the user enters something that is not a number, such as AA, scanf() leaves the offending input in the standard input buffer and returns 0, finally if the end of file is reached (the user types ^Z enter in windows or ^D in unix), scanf() returns EOF.
if the input was not converted to a number, we enter the body of the if statement: input is consumed one byte at a time with getchar(), until either the end of file or a linefeed is read.
if getchar() returned EOF, we have read the entire input stream, no need to prompt the user for more input, you might want to output an error message before returning an error code.
otherwise, set your_choice to -1, an invalid value so the read of the code complains and prompts for further input.
Reading and discarding the offending input is necessary: if you do not do that, the next input statement scanf(" %c", &ch); would read the first character of the offending input instead of waiting for user input in response to the Would you like to play again? (Y/N)?: prompt. This is the explanation for the behavior you observe.

Related

User input with scanf using while loop

I'm having a problem with multiple characters while using a while loop. I'm writing a code that would direct the user to a new function based on the input of either "y" or "n". When I scanf for one character it works fine; however, when the user types in multiple characters the while loop repeats.
#include <stdio.h>
int main()
{
char x;
printf("type in letter n or y\n");
scanf("%c", &x);
while (x!= 'Y' && x!='N' && x!= 'n' && x!='y')
{
printf("Invalid, please type Y/N to continue: \n");
scanf(" %c", &x);
}
if (x== 'Y' || x == 'y')
{
printf("y works");
}
if (x =='N' || x =='n')
{
printf("n works");
}
}
For example, if I type in hoyp, it would say "Invalid, ..." 2 times and then the "y works" would be written on the third line. How can the code be changed so that the invalid would only be said once, and the user must input again to allow the program to continue?
This is how scanf behaves. It keeps reading in all the characters you've entered. You can accept a string as input first using fgets and extract and check only its first character. fgets allows you to specify the exact number of characters to be read. I have first declared a char array of size 4096. This will work when the input is up to 4095 characters. You can adjust the size as per your needs.
#include <stdio.h>
int main()
{
char x, buffer[4096];
printf("type in letter n or y\n");
fgets(buffer, 4096, stdin);
x = buffer[0];
while (x!= 'Y' && x!='N' && x!= 'n' && x!='y')
{
printf("Invalid, please type Y/N to continue: \n");
fgets(buffer, 4096, stdin);
x = buffer[0];
}
if (x== 'Y' || x == 'y')
{
printf("y works");
}
if (x =='N' || x =='n')
{
printf("n works");
}
}
Here is my approach to the problem:
I have used fgets() instead of scanf(). See why
here.
I have used the suggestion by users jamesdlin and M.M in this question to solve the repeated printing issue when the input is more than one character or if the input is empty. I encourage you to read the whole thread to know more about this issue.
(Optional) Used some extra headers for better code readability in the loop conditions. I think the fgets() could be used in the condition of the while() but I got used to the pattern I have written below.
Edit: added a condition to reject inputs with length > 1. Previously, inputs that starts with 'y' or 'n' will be accepted (and are interpreted as 'y' or 'n' respectively) regardless of their length.
#include <stdio.h>
#include <stdbool.h>
#include <ctype.h>
void clearInput();
int main()
{
// allocate space for 'Y' or 'N' + '\n' + the terminator '\0'
// only single inputs will be accepted
char _inputbuff[3];
char choice;
bool isValidInput = false;
while(!isValidInput) {
printf("Please enter your input[y/n]: ");
// use fgets() instead of scanf
// this only stores the first 2 characters of the input
fgets(_inputbuff, sizeof(_inputbuff), stdin);
// don't accept empty input to prevent hanging input
if(_inputbuff[0] == '\n') {
printf("Empty input\n");
// go back to the top of the loop
continue;
}
// input is non-empty
// if the allocated space for the newline does not
// contain '\n', reject the input
if(_inputbuff[1] != '\n') {
printf("Input is more than one char.\n");
clearInput();
continue;
}
choice = _inputbuff[0];
// printf("The input is %c\n", choice);
// convert the input to uppercase for a 'cleaner' code
// during input validation
choice = toupper(choice);
// the input is not 'Y' or 'N'
if(choice != 'Y' && choice != 'N') {
printf("Please choose from Y or N only.\n");
// go back to the top of the loop
continue;
}
// the input is 'Y' or 'N', terminate the loop
isValidInput = true;
}
// conditions for 'Y' or 'N'
if(choice == 'Y') {
printf("The input is Yes.\n");
return 0;
}
if(choice == 'N') {
printf("The input is No.\n");
return 0;
}
}
void clearInput() {
int _clear;
// clear input stream to prevent repeated printing of invalid inputs
while ((_clear = getchar()) != '\n' && _clear != EOF ) { }
}
(This is my first time answering a question and it has been a while since I have used C so feel free to give suggestions/corrections regarding my answer. Thanks!)

Character input with %d causing infinite loop [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
int valid=0, running=1;
printf("\n1. Generate\n2. Retrieve");
while(!valid){
printf("\n\nEnter choice> ");
scanf("%d", &c);
if(c==1){
valid=1;
generate();
while(running){
printf("\n\nPress Y to generate again. Press N to retrieve> ");
scanf(" %c", retry);
if(retry == 'Y' || retry == 'y'){
idx++;
generate();
}else if(retry == 'N' || retry == 'n')
running = 0;
else
printf("Invalid input. Try again.");
}
retrieve();
}else if(choice==2){
valid = 1;
retrieve();
}else
printf("Invalid input. Try again");
}
Here the user should enter either 1 or 2. If the user enters any other number or character then I want to ask the user to input again. The program works fine if the user enters any other number like 5/6/7 etc. But if the user enters a character the program goes into an infinite loop. I can break the loop with a scanf status check but then the program stops. Instead, I want to prompt the user to input again if he enters anything except 1 or 2.
scanf returns the number of successful input assignments, or EOF on end of file or error. You should get in the habit of checking this return value. In this case of scanf( "%d", &c ), you should expect a return value of 1 on a successful input.
The %d conversion specifier tells scanf to skip over any leading whitespace, then read characters up to the first character that isn't a decimal digit, leaving that character in the input stream.
Example - suppose you enter "12.3" as an input. scanf( "%d", &c ) will read, convert, and assign the "12" portion of the input to c and return 1. The ".3" portion of the input is left in the input stream.
If you call scanf( "%d", &c ) again, the first thing it sees is that '.' character, so it immediately stops reading (you have a matching failure).
Since no input was actually read, nothing gets assigned to c and scanf returns 0. This will keep happening until you remove that '.' character with some other input operation like getchar() or scanf( "%*c" ), etc.
You should always check the result of scanf to make sure you read as many items as you expect:
int r = 0;
do
{
r = scanf( "%d", &c );
if ( r == EOF )
{
// end of file or error signaled on the input stream; in this case we
// just exit the program
exit( 0 );
}
else if ( r == 0 )
{
// matching failure - there's a bad character in the input stream
// remove it with getchar and try again
getchar();
}
} while( r != 1 );
// at this point we either have good input or have already exited the program
Apparently, using scanf with %d, but input characters causes buffer issues. See code below for one way to avoid the problem:
#include <stdio.h>
#include <stdlib.h>
void generate(){
printf("generate\n");
}
void retrieve(){
printf("retrieve\n");
}
int main(){
int valid=0, running=1, idx=0, c=-100;
char retry;
char s[25];
printf("\n1. Generate\n2. Retrieve");
while(!valid){
printf("\n\nEnter choice> ");
scanf("%s", s);
c = atoi(s);
if(c==1){
valid=1;
generate();
while(running){
printf("\n\nPress Y to generate again. Press N to retrieve> ");
while(scanf(" %c", &retry)==0);
if(retry == 'Y' || retry == 'y'){
idx++;
generate();
}else if(retry == 'N' || retry == 'n')
running = 0;
else
printf("Invalid input. Try again.");
}
retrieve();
}else if(c==2){
valid = 1;
retrieve();
}else
printf("Invalid input. Try again");
}
return(0);
}
One possible execution is shown below:
1. Generate
2. Retrieve
Enter choice> Bad
Invalid input. Try again
Enter choice> Worse
Invalid input. Try again
Enter choice> 1
generate
Press Y to generate again. Press N to retrieve> Y
generate
Press Y to generate again. Press N to retrieve> n
retrieve

How to accept ";' semicolon, as input, and not execute the next line of codes?

Here's a small portion of a practice I'm doing preventing erroneous inputs.
while(1) {
printf("Choose From 1 to 7 ");
if( scanf("%d", &nNum ) != 1) {
printf("Please only choose from the numbers 1-7.");
fgets(sErraticInputs, 100 , stdin);
} else if (nNum > 7 || nNum <= 0) {
printf("Please only choose from the numbers 1-7.");
} else {
break;
}
}
I was doing a good job, until I entered "6;p". It executed the 6 portion and ran correctly, but technically speaking it should have taken the whole thing as the input, and proceeded with the error message.
First of all I don't think the posted code can give the said result. The break statement will end the while(1) when 6 has been read so there will not be printed an error message.
If we assume that the break isn't part of your real code this is what happens:
When scanf is told to read an integer, it will continue reading from the input stream as long as the next character (together with the previous read characters) can be converted into an integer. As soon as the next character can not be used as part of an integer, scanf will stop and give you the result of what it has parsed so far.
In your case the input stream contains
6;p\n
So scanf will read the 6 and stop (i.e. return 6). The input stream now contains:
;p\n
Consequently this will be the input for your next scanf and cause the input error, you saw.
One way to solve this would be to flush stdin after all scanf - both on success and on failure:
nNum = 0;
while(nNum != 7) // Just as an example I use input 7 to terminate the loop
{
printf("Choose From 1 to 7 ");
if( scanf("%d", &nNum ) != 1 || nNum > 7 || nNum <= 0)
{
printf("Please only choose from the numbers 1-7.");
}
else
{
printf("Valid input %d\n", nNum);
// **************************** break;
}
fgets(sErraticInputs, 100 , stdin); // Always empty stdin
}
note: Using fgets with size 100 doesn't really ensure a complete flush... you should actually use a loop and continue until a '\n' is read.
With the change above input like 6;p will be taken as a valid input with value 6 and the ;p will be thrown away.
If that's not acceptable, you could drop the use of scanf and do the parsing yourself. There are several options, e.g. fgets or fgetc
The example below uses fgetc
#include <stdio.h>
#include <stdlib.h>
int get_next()
{
int in = fgetc(stdin);
if (in == EOF) exit(1); // Input error
return in;
}
void empty_stdin()
{
while(get_next() != '\n') {};
}
int main(void) {
int in;
int nNum = 0;
while(nNum != 7)
{
printf("Choose From 1 to 7 \n");
in = get_next();
if (in == '\n' || in <= '0' || in > '7') // First input must be 1..7
{
printf("Please only choose from the numbers 1-7.\n");
if (in != '\n') empty_stdin();
}
else
{
nNum = in - '0';
in = get_next();
if (in != '\n') // Second input must be \n
{
printf("Please only choose from the numbers 1-7.\n");
empty_stdin();
}
else
{
printf("Valid input: %d\n", nNum);
}
}
}
return 0;
}
This code will only accept a number (1..7) followed by a newline
Here's why the "whole thing" is not taken as the input. From the man pages:
The format string consists of a sequence of directives which describe
how to process the sequence
of input characters. If processing of a directive fails, no further input is read, and scanf()
returns. A "failure" can be either of the following: input failure, meaning that input characters
were unavailable, or matching failure, meaning that the input was inappropriate...
Here's the full text. Have a look at this as well.
One approach would be to read in the whole input using fgets and check whether the length of the input is greater than 1. For an input of length 1, check if the input is a number and so on...

usage of do while loop for yes no prompt in c

I am learning to code in c online and was trying this program to use the do while loop
This is a program for a dice game
A player can enter the game with minimum amount of 1000.
A player can take any number of chances. If the value of the dice is 1, the player leaves the game with half the amount.
If the value is other than 1, the total amount is raised by multiple of 100*value of dice.
If the player quits, he/she leaves the game with amount he/she has got.
#include <stdio.h>
#include <stdlib.h>
int main () {
int cash, dice;
char ch;
printf ("cash\n");
scanf ("%d", &cash);
if (cash < 1000) {
printf ("No\n");
exit (0);
}
else {
do {
printf ("dice\n");
scanf ("%d", &dice);
while (dice < 1 || dice > 6) {
printf ("\n invalid");
printf ("dice\n");
scanf ("%d", &dice);
}
if (dice == 1) {
cash = cash / 2;
break;
}
cash = (dice * 100) + cash;
printf ("Do you want to continue");
scanf ("%c", &ch);
} while (ch == 'y');
}
printf ("won=%d", cash);
return 0;
}
This program isn't accepting the y or n input.
It displays the statement do you want to continue
and directly goes to won statement.
Your second scanf is consuming the '\n' char left into stdin by the dice one
Simply adding a space before format specifier you can consume all chars in stdin before accempting the character inserted by user:
scanf (" %c", &ch);
In a scanf() format, a blank, tab or newline means 'skip white space if there is any to skip'.
You have to use a space before %c
scanf(" %c", &ch);
changing your scanf will solve all the problems
scanf(" %c",&ch); //notice space
printf ("dice\n");
scanf ("%d", &dice);
This has left '\n' in the buffer and your second call to scanf reads this '\n' instead of 'n' and continues. You must read remaining '\n' before reading character.
man scanf(3):
The format string consists of a sequence of directives which describe how to process the sequence of input characters. If processing of a directive fails, no
further input is read, and scanf() returns. A "failure" can be either of the following: input failure, meaning that input characters were unavailable, or match‐
ing failure, meaning that the input was inappropriate (see below).
A directive is one of the following:
· A sequence of white-space characters (space, tab, newline, etc.; see isspace(3)). This directive matches any amount of white space, including none, in the
input.
· An ordinary character (i.e., one other than white space or '%'). This character must exactly match the next character of input.
· A conversion specification, which commences with a '%' (percent) character. A sequence of characters from the input is converted according to this speci‐
fication, and the result is placed in the corresponding pointer argument. If the next item of input does not match the conversion specification, the con‐
version fails—this is a matching failure.
There are two options.
scanf (" %c", &ch);
or
(void) getchar ();
scanf ("%c", &ch);
The biggest problem you have is not accounting for the '\n' left in your input buffer (stdin here) that results from the user pressing Enter after entering dice as the %c format specifier will happily take the '\n' as the user-input for ch.
The next problem is ch should be and int not char or you will never be able to test/trap EOF.
Further, you fail to validate the return of scanf to confirm any of the conversions are valid.
Given the problems associated with taking user input with scanf, you are better served taking user input with fgets and reading the entire line of user input into a buffer of sufficient size for the input and then parsing your numeric values from the buffer with sscanf (or simply subtracting '0' for a single digit).
Putting those pieces together, you could do something like the following:
#include <stdio.h>
#include <stdlib.h>
#define BUFLEN 128
int main () {
int ch, cash, dice;
char buf[BUFLEN] = "";
printf ("cash: ");
if (!fgets (buf, BUFLEN, stdin)) { /* read/validate cash */
fprintf (stderr, "error: invalid input - cash.\n");
return 1;
}
if (sscanf (buf, "%d", &cash) != 1) { /* parse cash from buf */
fprintf (stderr, "error: invalid conversion - cash.\n");
return 1;
}
if (cash < 1000) {
printf ("No\n");
return 1;
}
do {
ch = 'n'; /* set/reset ch to exit each iteration */
printf ("dice: ");
if (!fgets (buf, BUFLEN, stdin)) { /* read/validate dice */
fprintf (stderr, "error: invalid input - dice.\n");
break; /* break on EOF */
}
/* parse/validate dice */
if (sscanf (buf, "%d", &dice) != 1 || dice < 1 || dice > 6) {
fprintf (stderr, "error: invalid conversion - dice.\n");
ch = 'y'; /* set ch to 'y' */
continue; /* prompt again, etc */
}
if (dice == 1) {
cash = cash / 2; /* note: truncated division */
break;
}
cash = (dice * 100) + cash;
printf ("\nDo you want to continue? (y/n): ");
if (fgets (buf, BUFLEN, stdin))
ch = *buf; /* simply assign first char */
else
break; /* exit loop on EOF */
} while (ch == 'y');
printf ("\nwon = %d\n\n", cash);
return 0;
}
(note: tests to validate less than 128 chars entered for each input omitted)
Example Use/Input
$ ./bin/rfmt
cash: 1200
dice: 7
invalid dice.
dice: 5
Do you want to continue? (y/n): y
dice: 2
Do you want to continue? (y/n): y
dice: 6
Do you want to continue? (y/n): n
won = 2500
Look over all the answer and decide whether you want to go with scanf or fgets/sscanf. Both are doable, you just lose a bit of flexibility when you couple both your read and parse in a single scanf call. Let me know if you have any questions.

C user input only integers, return value from scanf

I am trying to get some user input, and I want to make sure that they enter integers, and if they don't I will just ask them to type again (loop until they get it right).
I have found a lot of different approaches to do this; some more complicated then others. But I found this approach which seems to work.
But I just don't really get why this is tested in the code:
scanf("%d%c", &num, &term) != 2
I can understand that scanf outputs the number of items successfully matched and assigned, but I don't get why it outputs 2 if it is an integer.
The code in C is:
int main(void)
{
int num;
char term;
if (scanf("%d%c", &num, &term) != 2 || term != '\n')
printf("failure\n");
else
printf("valid integer followed by enter key\n");
}
Trying to put it in loop:
int main(void){
int m, n, dis;
char m_check, n_check, dis_check;
do{
m = 0; n = 0; dis = 0;
m_check = ' '; n_check = ' '; dis_check = ' ';
printf("Please enter number of rows m in the matrix (integer): ");
if(scanf("%d%c", &m, &m_check) !=2 || m_check != '\n')
m_check = 'F';
printf("Please enter number of columns n in the matrix (integer): ");
if(scanf("%d%c", &n, &n_check) !=2 || n_check != '\n')
n_check = 'F';
printf("Should the random numbers come from a uniform or normal distribution?...
Please press 1 for uniform or 2 for normal: ");
if(scanf("%d%c", &dis, &dis_check) !=2 || dis_check != '\n' || dis != 1 || dis !=2)
dis_check = 'F';
}while(m_check == 'F' || n_check == 'F' || dis_check == 'F');
I've tried just inputting m = 3, n = 3, dis = 2, and then the loop just starts over and asks me to input number of rows. And if I when asked for this press f or something it just start looping like crazy over the printf-statements :)
scanf returns the number of fields it converted. You have the format string %d%c; it has:
%d - first field
%c - second field
so scanf returns 2.
If the user enters a number e.g. 123 and presses Enter, your num will be equal to 123, and term will be \n.
If the user enters a number with garbage at the end, e.g. 123garbage and presses Enter, your num will be equal to 123, the term will be g, and arbage\n will remain in the input buffer.
In both cases, scanf read an int and a char, so it returns 2.
A different example: the user enters garbage123. In this case, scanf will fail to read an integer, and return 0.
So your code checks for two different forms of incorrect output.
Entering an integer will match to the formatter %d and will assign the value to the variable num. The %c formatter will take the newline character ('\n') and store it in the variable term. scanf returns 2 cause 2 elements where correctly assigned (num and term)
If you don't enter an integer the formatter %d won't match correctly, and scanf won't return 2, producing a failure
Edit: Your do-while loop goes crazy cause your conditions in the last scanf are wrong (dis != 1 || dis !=2). It should be
if(scanf("%d%c", &dis, &dis_check) !=2 || dis_check != '\n' || (dis != 1 && dis !=2))
scanf("%d%c", &num, &term) != 2
This checks the return value of scanf . It will return number of arguments correctly matched . So , if a integer and a character is entered , they are store in vairables num and term and scanf returns 2 .
scanf will not return 2 if both the arguments are not correctly matched , in that case failure will be displayed as output.
Oops - now see OP wants to know why approached failed and was not necessarily looking for a how-to do it. Leaving this up as a reference as it does assert the fundamental problem: scanf(). Do not use it for user input.
If code truly needs to "get some user input, and wants to make sure that they enter integers", then use fgets() to get the line and then process the input.
Mixing user input with scanning/parse invariably results in some user input defeating the check, extra user input in stdin or improperly waiting for more input.
// true implies success
bool Read_int(int *value) {
char buffer[100];
if (fgets(buffer, sizeof buffer, stdin) == NULL) return false; // EOF or error
// Now parse the input
char *endptr;
errno = 0;
long number = strtol(buffer, &endptr, 10);
if (errno) return false; // long overflow
if (number < INT_MIN || number > INT_MAX) return false; // int overflow
if (endptr == buffer) return false; // No conversion
while (isspace((unsigned char) *endptr)) endptr++;
if (*endptr) return false; // non-white-space after number
*value = (int) number;
return true;
}

Resources