usage of do while loop for yes no prompt in c - 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.

Related

Clearing bad input

do {
printf("Enter endpoints of interval to be integrated (low hi): ");
test = scanf("%lf %lf", &low, &hi);
if (test != 2) {
badInput(low);
badInput(hi);
printf("Error: Improperly formatted input");
}
else if(low > hi)
printf("Error: low must be < hi\n");
} while ((test != 2 || low > hi));
In this code I'm trying to eliminate user input error. Currently, my issue is if the user enters letters instead of numbers the prompt just repeat without letting new user input.
What would I need to put in the function badInput in order to avoid this?
Currently, my issue is if the user enters letters instead of numbers the prompt just repeat without letting new user input.
The scanf() expects two double input from user:
test = scanf("%lf %lf", &low, &hi);
When you give letters instead of number as input to scanf(), it does not consume them because they do not match with the given format string and leaves them in the input buffer. You must have note that when you are giving letters instead of numbers, the scanf() must be returning 0 because it does not consume them. Since, scanf() does not consume the invalid input, in next iteration of loop scanf() find the unconsumed invalid input in the buffer and again skip them. Thats why your program does not stop for input when giving letters as input. To solve this problem, you have to flush the invalid input out of the buffer. You can do:
do {
printf("Enter endpoints of interval to be integrated (low hi): ");
test = scanf("%lf %lf", &low, &hi);
if (test != 2) {
badInput(low);
badInput(hi);
printf("Error: Improperly formatted input");
int c;
while((c = getchar()) != '\n' && c != EOF) // <=== This loop read the extra input characters and discard them
/* discard the character */;
}
......
......

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...

scanf not working on invalid input

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.

Verifying input, and clearing keyboard buffer

How would I be able to clear the buffer if a character or more is entered in this block of code.
int x = 1;
float grade = 0.0;
do
{
printf ("Enter a grade for quiz %d: ", x);
scanf ("%f", grade);
if (grade >= 1 && grade <= 10) break;
printf ("Entry not valid. Please try again\n");
} while (1);
My instructor is insisting that we use fflush(stdin). This I know wont work, and I called him out on it. What other ways could I just to validate if a number is entered or not.
void flush_stdin ()
{
char c;
do
{
c = get(stdin);
}while(c != EOF && c != '\n');
}
I use this in my own code, basically, read every character in stdin until you encounter a sign that it is now empty.
I ll also urge to avoid scanf, but prefer using fread with sscanf, so you can limit the number of input character, wich avoid buffer overflow and other nasty things.
Rather than attempting to flush the input buffer on a bad read, always read a whole line using fgets()/sscanf().
Example Read_long
do
{
char buf[50];
printf ("Enter a grade for quiz %d: ", x);
if (fgets(buf, sizeof buf, stdin) == NULL)
Handle_EOForIOError();
if (sscanf(buf, "%f", grade) == 1 && grade >= 1 && grade <= 10)
break;
printf ("Entry not valid. Please try again\n");
} while (1);

Scan single character C

/*
Program to calculate trip and plan flights
*/
#define TRIP 6
#define DEST 1
#include <stdio.h>
int error_dest(int type_num, int cont_num, int dest_code, int check);
int main(void)
{
int check, type_num, cont_num, index, i, dest_code, trip_num, row, col;
int travelint[TRIP][DEST], travelarea[TRIP];
char area_code, S, M, L, N, P, K, R, C, U, W, O;
trip_num = 7;
while (trip_num > TRIP)
{
printf("Please enter the number of trips:");
scanf("%d", &trip_num);
if ( trip_num < TRIP)
{
printf("Valid trip number. Please proceed to enter destination code.\n");
}
else
{
printf("Invalid trips. Please enter no more then 6 trips.\n");
}
}
/*********************************************************************************/
for (i=0; i < trip_num ; i++) /*destination code input*/
{
printf("Please enter destination code:");
scanf("%d", &dest_code); /*input of destination code*/
check = error_dest(type_num, cont_num, dest_code, check);
if (check == 2)
{ travelint[i][0]=dest_code; }
else
{
while (check == 1)
{
printf("Please enter destination code:");
scanf("%d", &dest_code); /*input of destination code*/
check = error_dest(type_num, cont_num, dest_code, check);
if (check == 2)
{ travelint[i][0]=dest_code; }
}
}
printf("Please select from the following that best describes your destination:\n");
printf("S Small city - population under 50,000\n");
printf("M Medium city - population between 50,000 and 500,000\n");
printf("L Large city - pop. over 500,000\n");
printf("N Natural formation like a mountain, a lake, a cave, a geyser, a fjord, a canyon, etc.\n");
printf("P Designated park or reserve such as a wildlife refuge, a national park, a bioreserve, or a protected marine area\n");
printf("K Man made landmark like the Great Wall of China, the Taj Mahal, or Stonehenge\n");
printf("R State or province or region of a country\n");
printf("C Whole country\n");
printf("U Multiple countries like traveling through Europe\n");
printf("W Ocean voyage\n");
printf("O Any other type of destination - such as visiting the sites of the seven wonders of the world\n");
printf("Please enter the Area Letter code:");
scanf("%c", &area_code);
}
/*******************************************************************************/
/*print for destination_code*/
for (row = 0; row < trip_num; row++)
{
for (col=0; col < DEST; col++)
printf("Trip[%d] = %d\n", row+1, travelint[row][col]);
}
return 0;
}
error_dest(type_num, cont_num, dest_code, check)
{
cont_num = dest_code / 10000; /*math for error check*/
type_num = dest_code/1000 - cont_num*10;
if ( (cont_num <= 7) && (cont_num > 0) && (type_num <= 5) && (type_num >=0) )
{ /* loop for checking destination code*/
check = 2 ;
return check;
}
else
{
printf("%d is a invalid code\n", dest_code);
check = 1;
return check;
}
}
for some strange reason at the scanf("%c", &area_code); it just runs ahead and print the dest_code array without letting me input any character and I'm not sure what exactly I am doing wrong.
If you're looking to grab only one character, perhaps it would be better to use getchar() instead of scanf()?
Basically what's happening is this: you print the "Please enter the number of trips" message to the screen. The user types in 4 and then hits the enter key, which means the stdin buffer looks like this: "4\n". You then call scanf with the "%d" format string. scanf looks at the stdin buffer, and sees the 4. It looks at the next character, which is the newline, and sees it's not part of a number (as %d specifies), so it is done fulfilling the format string and leaves the file pointer at the newline. It converts the char '4' to an integer 4 and places it in trip_num and returns.
The next time you call scanf, it picks up where it left off at the newline. The format string this time is "%c", so it just grabs the next character from the buffer which is currently the newline ("\n"), places it in dest_code, and returns. If you want the scanf function to skip over the whitespace in this case, you have to explicitly tell it by adding a space before the "%c" format for the second scanf (destination code). Then scanf will skip over all whitespace (including that newline) until it encounters a non-whitespace character that it places in dest_code.
TL;DR: Change the second scanf call to scanf(" %c", &dest_code). And fix the other errors others have pointed out so other bugs won't manifest.
You may print area_code after scanf, I guess it may be '\n' which is the last character of the dest_code line you entered.
You should empty the buffer before reading a character from stdin:
int c = 0;
while (c != '\n' && c != EOF)
{
c = getchar();
}
then you can read your character using scanf or replace it with getchar.
This may or may not help, but previously stated you probably need to put the getchar() into the while loop. You may also need the fgets to grab the stdin from the keyboard.
while(1){
printf("Enter Message Type:");
fflush(stdout) ;
// scan msg.hdr from received message.
scanf("%d", &(msg.m_hdr));
while(getchar() != '\n'){}
printf("Enter your Message:");
fflush(stdout);
// grab data from keyboard
fgets(msg.m_data, sizeof(msg.m_data), stdin);
Use "fflush(stdin)" before you enter the character, i.e. before the "printf" statement for the character. It will flush out the input buffer and thus you can scan the desired character. Or simply give a Space before the "%c" command. Like---------- scanf(" %c", &area_code); ---------------

Resources