How to properly get a y/n input in C - c

So, I'm a C and programming newcomer and I'm trying to write a function to abstract y/n choices in a program that uses a lot of them, as of now I have it like this:
void chooser (char *choice)
{
while (1)
{
*choice='\0';
*choice=getchar();
while(getchar()!='\n');
if(*choice == 'y' || *choice == 'n')
break;
}
}
The second getchar consumes input in excess in the stdin so that it shouldn't be susceptible to the user typing gibberish.
The only problem is that if I type "ywhatever" or "nwhatever" the first getchar still captures "y" and "n" and passes it to the main.
I would like to have it so that the user has to type "y" or "n" and nothing else.
I could try ditching getchar and switching to fgets for capturing and sscanf for parsing, which is how I prefer to get strings, but, dunno, it's just a single character, I would prefer not to overcomplicate things.
Is there a way to scan stdin after user input to see if it contains more than one alphabetic character?
EDIT:
In the end, I went for this:
int chooser ()
{
char buffer[MAXIN];
while (1)
{
printf("y/n: ");
if (fgets(buffer, sizeof(buffer), stdin)!=NULL)
{
if (buffer[0] == 'y' || buffer[0] == 'n')
{
if (buffer[1]=='\n')
{
break;
}
else
{
while(getchar()!='\n');
}
}
}
}
if (buffer[0]=='y')
{
return 1;
}
if (buffer[0]=='n')
{
return 0;
}
}
It seems to be doing exactly what I need it to do, is there something else I should adjust? Does a fgets from the standard input need a if(fgets(...)!=NULL) check?

What's wrong with this solution:
#include <stdio.h>
void chooser(char *choice)
{
char buffer[200];
while (1)
{
fgets(buffer, sizeof(buffer), stdin);
if (buffer[0] == 'y' || buffer[0] == 'n')
break;
}
*choice = buffer[0];
}
int main(int argc, char **argv)
{
while (1)
{
char yn;
chooser(&yn);
if (yn == 'y' || yn == 'n')
printf("User choice: %c\n", yn);
}
}
That's not exactly "overcomplicated"
There is still room for improvement, e.g the EOF condition is not handled at all here.
Example of execution:
abc
def
y
User choice: y
n
User choice: n
yes
User choice: y
no
User choice: n
noooo
User choice: n

Related

How to take input until enter is pressed twice?

I want to break this loop when the user press enters twice. Meaning, if the user does not enter a character the second time, but only presses enter again, the loop must break.
char ch;
while(1) {
scanf("%c",&ch);
if(ch=='') { // I don't know what needs to be in this condition
break;
}
}
It is not possible to detect keypresses directly in C, as the standard I/O functions are meant for use in a terminal, instead of responding to the keyboard directly. Instead, you may use a library such as ncurses.
However, sticking to plain C, we can detect newline characters. If we keep track of the last two read characters, we can achieve similar behavior which may be good enough for your use-case:
#include <stdio.h>
int main(void)
{
int currentChar;
int previousChar = '\0';
while ((currentChar = getchar()) != EOF)
{
if (previousChar == '\n' && currentChar == '\n')
{
printf("Two newlines. Exit.\n");
break;
}
if (currentChar != '\n')
printf("Current char: %c\n", currentChar);
previousChar = currentChar;
}
}
Edit: It appears that the goal is not so much to detect two enters, but to have the user:
enter a value followed by a return, or
enter return without entering a value, after which the program should exit.
A more general solution, which can also e.g. read integers, can be constructed as follows:
#include <stdio.h>
#define BUFFER_SIZE 64U
int main(void)
{
char lineBuffer[BUFFER_SIZE];
while (fgets(lineBuffer, BUFFER_SIZE, stdin) != NULL)
{
if (lineBuffer[0] == '\n')
{
printf("Exit.\n");
break;
}
int n;
if (sscanf(lineBuffer, "%d", &n) == 1)
printf("Read integer: %d\n", n);
else
printf("Did not read an integer\n");
}
}
Note that there is now a maximum line length. This is OK for reading a single integer, but may not work for parsing longer input.
Credits: chux - Reinstate Monica for suggesting the use of int types and checking for EOF in the first code snippet.
You can store the previous character and compare it with the current character and enter, like this:
char ch = 'a', prevch = '\n';
while(1){
scanf("%c",&ch);
if((ch=='\n') && (ch == prevch)){// don't know what needs to be in this condition
break;
}
prevch = c;
}
Note that the previous character by default is enter, because we want the program to stop if the user hits enter at the very start as well.
Working like charm now
char ch[10];
while(1){
fgets(ch, sizeof ch, stdin);
if(ch[0]=='\n'){
break;
}
}

While loop causing many issues with program

void answerme();
int main() {
char *answer = malloc (MAX_NAME_SZ);
....
printf ("\nWould you like to begin? [Y/N]");
fgets (answer, MAX_NAME_SZ, stdin);
answerme();
if(*answer == 'y' || *answer == 'Y'){
getinfo();
printf("\nprogram starting now...");
}
else if(*answer == 'n' || *answer == 'N'){
printf("\nThank you, program will close now....");
return 0;
}
...
} //end of main
void answerme(){
char *answer = malloc (MAX_NAME_SZ);
while(*answer !='n' && *answer != 'N' && *answer != 'y' && *answer != 'Y'){
printf("\nPlease enter [Y], or [N]");
fgets (answer, MAX_NAME_SZ, stdin);
}
};
What the point of this while loop or the whole function is that for it to check if the user has answered the question with a y/n rather than another random key. I want this while loop to continue asking the user for a Y/N input until the user inputs it. However for some reason when this program is run, the first step asks you if you would like to begin the program, and if you do answer Y, it will for some reason tell you "please enter Y or N" even though you did enter the right answer, and then when you do enter for example "n" or even any other random letter it will still let you through. So it seems like it registers the input but for some reason it still asks runs the while loop instead of skipping to the if(answer == Y) or the if(answer ==N).
Does anyone know what could be the reason this is happening?
Also once the user says "Y" and begins the program there will be a message asking the user to input certain information and this information gets stored into a structure which I created (not shown in the code), however with this while loop, this somehow gets skipped. If I take off this while loop, the whole program works fine, but of course the user will be able to skip through steps of the program without strictly inputing what I've asked of him.
If there's any better alternative way of restricting the user into only inputing what I've asked, please do enlighten me on that as this has been causing me issues and headaches for the past 3 days. Thank you !
The problem is that you set a variable *answer in the function and there is another one in the main program. However, it looks like they are expected to be the same variable.
To fix this, declare only one and share it between the two functions. Do that by declaring it outside any function, or pass it from main to the subfunction. Note that it should be malloc() only once.
Example of the parameter passing technique is:
void answerme (char *answer)
{
while (*answer !='n' && *answer != 'N' &&
*answer != 'y' && *answer != 'Y')
{
printf ("\nPlease enter [Y], or [N]");
fgets (answer, MAX_NAME_SZ, stdin);
}
}
int main()
{
char *answer = malloc (MAX_NAME_SZ);
....
printf ("\nWould you like to begin? [Y/N]");
fgets (answer, MAX_NAME_SZ, stdin);
answerme(answer);
if (*answer == 'y' || *answer == 'Y')
{
getinfo();
printf("program starting now...\n");
}
else
if (*answer == 'n' || *answer == 'N')
{
printf("Thank you, program will close now.\n");
return 0;
}
...
} //end of main
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define SIZE 10
void answerme();
int main() {
char answer[SIZE]="0";
printf ("\nWould you like to begin? [Y/N]");
scanf(" %s",answer);
if((strcmp(answer,"y")==0) || (strcmp(answer,"Y")==0))
{
printf("Answer is y\n");
printf("\nprogram starting now...");
answerme();
}
else
{
printf("Wrong input..exiting\n");
exit(1);
}
return 0;
}
void answerme()
{
char answer[SIZE]="0";
do
{
printf("\nPlease enter [Y], or [N]");
scanf(" %s",answer);
printf("You entered %s\n",answer);
}while((strncmp(answer,"y",1)!=0) && (strncmp(answer,"Y",1)!=0) && (strncmp(answer,"n",1)!=0) && (strncmp(answer,"N",1)!=0));
}

First input skipped, straight to next input

I'm facing a problem with my code of a simple login program. The problem I'm facing is when I use a switch case or if statement for the option of logging in as an Admin or a User, the input for username is skipped and goes directly to password, and no matter what I type it gives me my error message. Instead, I want it to receive my username first then the password. It works fine on its own if there is only code for either Admin OR User, only one but not when there are more than one. Please help. Note: I'm using the same functions for both admin and user just to check if it works. The picture shows the output.I'm a C newbie, so minimal jargon perhaps? Code as follows:
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
char username[18];
char pass[16];
void arequest()
{
printf("\nPlease Enter username:");
fflush(stdin);
gets(username);
printf("\nPlease Enter Password:");
fflush(stdin);
gets(pass);
}
void averify()
{
if (strcmp(username, "admin") == 0)
{
if (strcmp(pass, "apass") == 0)
{
printf("Successful Login");
_getch();
}
else
{
printf("Invalid Password");
_getch;
}
}
else
{
printf("Invalid Username");
_getch();
}
}
int choice;
int main()
{
printf("Welcome to Railway Reservation System");
printf("\n1.Admin \n2.User");
printf("\nPlease Enter your selection:");
scanf_s("%d", &choice);
if (choice == 1)
{
arequest();
averify();
}
else if (choice == 2)
{
arequest();
averify();
}
else
{
printf("Invalid Choice");
_getch();
return main;
}
return 1;
}
output
You are flushing the input stream with fflush(). fflush(stdin) is undefined behavior in most cases, and is at best implementation-dependent. To clear the extra characters from the input stream, consider writing a little function like this:
void clear_stream(void)
{
int c;
while ((c = _getch()) != '\n' && c != EOF)
continue;
}
Remove the calls to fflush(). You do not need to clear the stream after gets(username) since gets() discards the newline. Add a call to clear_stream() after this line in main():
scanf_s("%d", &choice);
There may be extra characters, including a newline, left in the input stream after the call to scanf_s(), and these need to be removed before trying to read user input again. In some cases scanf()_s (and scanf()) will skip over initial whitespaces in reading input, but _getch() and getchar() will not. This illustrates one of the dangers of using scanf().
printf("\nPlease Enter your selection:");
scanf("%d", &choice);
clear_stream();
Also, gets() is considered so dangerous that there is never a reason to use it for anything at all. Use fgets() instead. fgets() does keep the newline, where gets() discards it, so I often write my own version of gets() using fgets() that is safe:
char * s_gets(char *st, int n)
{
char *ret;
int ch;
ret = fgets(st, n, stdin);
if (ret) {
while (*st != '\n' && *st != '\0')
++st;
if (*st)
*st = '\0';
else {
while ((ch = getchar()) != '\n' && ch != EOF)
continue; // discard extra characters
}
}
return ret;
}
The library conio.h is nonstandard, as are the functions _getch() and scanf_s(). You should use the stdio.h functions getchar() and scanf(). The value returned by scanf() is the number of successful assignments, and you should check this to be sure that the input is as expected. In your program, if the user enters a letter at the selection prompt, no assignment is made, and the value of choice remains uninitialized. The code continues without handling this problem. choice could be initialized to some reasonable value, such as int choice = -1;. Alternatively, you can check the return value from scanf() to see if an assignment was made, and proceed accordingly.
I noticed that you are returning 1 from main(). You should return 0 unless there is an error. And, I see that you return main in the event of an invalid choice. Maybe you meant to return 1 here? And it appears that you have forgotten to #include <string.h> for the strcmp() function.
Finally, I don't understand why username, pass, and choice are global variables. This is a bad practice. These should be declared in main() and passed to functions as needed. It would be a good idea to #define the global constants MAXNAME and MAXPASS instead of hard-coding the array dimensions.
I didn't intend this to be a full-scale code review when I started, but that is what it turned into. Here is a revised version of your program that implements the suggested changes:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXNAME 18
#define MAXPASS 16
void clear_stream(void)
{
int c;
while ((c = getchar()) != '\n' && c != EOF)
continue;
}
char * s_gets(char *st, int n)
{
char *ret;
int ch;
ret = fgets(st, n, stdin);
if (ret) {
while (*st != '\n' && *st != '\0')
++st;
if (*st)
*st = '\0';
else {
while ((ch = getchar()) != '\n' && ch != EOF)
continue; // discard extra characters
}
}
return ret;
}
void arequest(char username[MAXNAME], char pass[MAXPASS])
{
printf("\nPlease Enter username:");
s_gets(username, MAXNAME);
printf("\nPlease Enter Password:");
s_gets(pass, MAXPASS);
}
void averify(char username[MAXNAME], char pass[MAXPASS])
{
if (strcmp(username, "admin") == 0)
{
if (strcmp(pass, "apass") == 0)
{
printf("Successful Login");
getchar();
}
else
{
printf("Invalid Password");
getchar();
}
}
else
{
printf("Invalid Username");
getchar();
}
}
int main(void)
{
char username[MAXNAME];
char pass[MAXPASS];
int choice;
printf("Welcome to Railway Reservation System");
printf("\n1.Admin \n2.User");
printf("\nPlease Enter your selection: ");
if (scanf("%d", &choice) == 1) {
clear_stream();
if (choice == 1)
{
arequest(username, pass);
averify(username, pass);
}
else if (choice == 2)
{
arequest(username, pass);
averify(username, pass);
}
else
{
printf("Invalid Choice: %d\n", choice);
getchar();
return 1;
}
} else {
clear_stream(); // stream has not yet been cleared
printf("Nonnumeric input");
getchar();
}
return 0;
}
EDIT
The OP mentioned in the comments that scanf() was causing problems in Visual Studio. Apparently Visual Studio tries to force the use of scanf_s(). The issue with this function is not that it is inherently bad, just that it is nonstandard. One solution might be to use the s_gets() function already added to the code to read the user selection into a character buffer, and then to use sscanf() to extract input. This has an advantage in that there is no need to call the clear_stream() function after s_gets(), because s_gets() cleans up after itself, so the clear_stream() function could now be removed altogether from the program. This can be accomplished with only a small change in main():
char choice_buffer[10];
int choice;
...
if (s_gets(choice_buffer, sizeof(choice_buffer)) &&
sscanf(choice_buffer, "%d", &choice) == 1) {
if (choice == 1)
...
} else {
printf("Nonnumeric input");
getchar();
}
s_gets() reads up to the first 9 characters (in this case) of a line of user input into choice_buffer, which is an array that will hold chars (there is more space in choice_buffer than is needed to hold a single digit choice and a '\0'). If there is an error, s_gets() returns a NULL pointer, otherwise a pointer to the first char of choice_buffer is returned. If the return value of s_gets() was non-NULL, then sscanf() assigns the first int stored in the buffer to choice. If no int is found in the string, sscanf() returns a value of 0, failing the conditional test.

Can't clear the stdin using fflush(stdin), after using getchar(), in an infinite for loop C prog

I have just started off with C programming and while I was trying to write a programme to accept only y or n characters I came across that
#include <stdio.h>
#include <stdlib.h>
int main()
{
char ch;
printf("Do you want to continue\n");
for (;;)
{
ch=getchar();
if (ch=='Y' || ch=='y')
{
printf("Sure!\n");
break;
}
else if (ch=='N'||ch=='n')
{
printf("Alright! All the best!\n");
break;
}
else
{
printf("You need to say either Yes/No\n");
fflush(stdin);
}
}
return(0);
}
When I run this code, and type in any other character other than Y/y or N/n, I receive the last printf statement (You need to say either Yes/No) as output twice.
I understand that this is happening because it considers enter, i.e, '\n' as another character.
Using fflush doesn't help as it's an infinite loop.
How else can I modify it so that the last statement is displayed only once?
You can use a loop to read any characters left using getchar():
ch=getchar();
int t;
while ( (t=getchar())!='\n' && t!=EOF );
The type of ch should int as getchar() returns an int. You should also check if ch is EOF.
fflush(stdin) is undefined behaviour per C standard. Though, it's defined for certain platforms/compilers such as Linux and MSVC, you should avoid it in any portable code.
Another option - use scanf ignoring white spaces.
Instead of ch=getchar();, just need scanf( " %c", &ch );
With this you can also get rid of fflush(stdin);
Like is said in my comment you should use int ch instead of char ch because the return type of getchar which is int.
To clean stdin you could do something like the following:
#include <stdio.h>
#include <stdlib.h>
int main(void){
int ch,cleanSTDIN;
printf("Do you want to continue\n");
for (;;)
{
ch = getchar();
while((cleanSTDIN = getchar()) != EOF && cleanSTDIN != '\n');
if (ch=='Y' || ch=='y')
{
printf("Sure!\n");
break;
}
else if (ch=='N'||ch=='n')
{
printf("Alright! All the best!\n");
break;
}
else
{
printf("You need to say either Yes/No\n");
}
}
return(0);
}
Any way a do while will probably do the job for you:
#include <stdio.h>
#include <stdlib.h>
int main(void){
char ch;
int check;
do {
printf("Do you want to continue: ");
if ((scanf("%c",&ch)) == 1){
while((check=getchar()) != EOF && check != '\n');
if ((ch == 'y') || (ch == 'Y')){
printf("Alright! All the best!\n");
break;
} else if((ch == 'n') || (ch == 'N')){
printf("You choosed %c\n",ch);
break;
}else{
printf("You need to say either Yes/No\n");
}
}else{
printf("Error");
exit(1);
}
}while (1);
return 0;
}
Output1:
Do you want to continue: g
You need to say either Yes/No
Do you want to continue: y
Alright! All the best!
Output2:
Do you want to continue: n
You choosed n
Or we can simply use another break; statement after the last printf().

C getchar() doesn't wait for input/ conditional loop doesn't int

I am trying to add a feature to my C console application calculator that prompts the user to decide whether they want to perform another calculation using: y or n, but in testing, getchar() refuses to wait for input and the program proceeds as though it has received valid input. The following is a minimal example of the feature:
main()
{
char newCalculation;
do{
lengthFormula(); /* main calculation formula */
printf("Would you like to do another calculation? (Y/N)");
newCalculation = getchar();
}while(tolower( newCalculation ) == 'y');
if(tolower(newCalculation) == 'n'){
exitProgram(); /* exit the program */
}
while(tolower(newCalculation) != 'n' && tolower(newCalculation) != 'y'){
printf("This is not a valid response.\n Please enter \"Y\"
if you want to do another calculation,
or enter \"N\" to exit.\n");
newCalculation = getchar();
}
return 0;
}
When I run this, the program does not wait for input after:
Would you like to do another calculation? (Y/N)
, but instead proceeds as though it has received invalid input. The result is that it spits out the prompt and the invalid input notice one after the other without a space:
Would you like to do another calculation? (Y/N)
This is not a valid response.
Please enter \"Y\" if you want to do another calculation, or enter \"N\" to exit.
If I enter a "y" after this, main() returns 0 and the program terminates.
Is someone able to see where I went wrong here?
Why won't the console wait for input at getchar()?
Why does valid input terminate the program after the first invalid response?
P.S.: Please don't tell me to "read a book" or shoo me away to Dennis Ritchie or one of the previous SO discussions on input. I've been poring over Richie's discussion of I/O, as well as similar texts from Lynda.com and Wiley, and none of the previous "it won't wait for input" posts addresses my issue as far as I can tell.
#simplicisveritatis Here is the modification of your code that I tried. Still have the same getchar issues.
int main(void)
{
/* local variable declaration */
char newCalculation = 'y';
/* main function */
/*if(tolower( newCalculation ) == 'y')
{
lengthFormula(newCalculation);
}*/
do
{
lengthFormula();
printf("Would you like to do another calculation? (Y/N)");
newCalculation = getchar();
if( tolower( newCalculation ) == 'n' )
{
exitProgram();
}
while( tolower( newCalculation ) != 'n' && tolower( newCalculation ) != 'y' )
{
printf("This is not a valid response.\n Please enter \"Y\" if you want to do another calculation, or enter \"N\" to exit.\n");
newCalculation = getchar();
}
}while( tolower( newCalculation ) == 'y' );
return 0;
}
Your code has a lot of problems:
main should be:
int main(void){return 0;}
You need to cast getchar (read about getchar) and should be:
newCalculation = (char)getchar();
Your approach on do{}while; + while{} is also wrong used.
Try the following:
#include<stdio.h>
#include<stdlib.h>
int main(void){
int validate;
char menu_choice;
validate = 0;
do{
printf("Would you like another go?(y/n):\t" );
if(scanf(" %c", &menu_choice ) == 1){
if((menu_choice=='y') || (menu_choice=='Y')){
printf("You choosed Yes\n\n\n");
validate = 1;
}else if((menu_choice=='n') || (menu_choice=='N')){
printf("You choosed No\n\n\n");
validate = 2;
}else{
printf("Wrong Input.\n\n\n");
validate = 0;
}
}
}while( validate == 0 || validate == 1);
printf("Goodbye\n");
return 0;
}
Include your three cases: exit condition, wrong input and calculate within the while loop:
main(){
do{
printf("Would you like to do another calculation? (Y/N)");
// get input
char newCalculation;
newCalculation = getchar();
// exit condition
if(tolower(newCalculation) == 'n'){
exitProgram(); /* exit the program */
}
// wrong input condition
else if(tolower(newCalculation) != 'n' && tolower(newCalculation) != 'y'){
printf("This is not a valid response.\n Please enter \"Y\"
if you want to do another calculation,
or enter \"N\" to exit.\n");
// you should clear the input stream from the wrong input
}
else{
// calculate
lengthFormula();
}
}while(tolower(newCalculation) == 'y');
return 0;
}
Why won't the console wait for input at getchar()?
Most probably, your function lengthFormula() reads input (e. g. by using scanf() or whatever), but doesn't read the line ending character \n from the input buffer. Then after returning from lengthFormula(), the getchar() has to read remaining content from the input buffer rather than requesting fresh input.
Why does valid input terminate the program after the first invalid
response?
That's because your
while(tolower(newCalculation) != 'n' && tolower(newCalculation) != 'y')
does the same after a response of y as after a response of n - it leaves the loop and gets to the following
return 0;

Resources