Not able to break from while loop using getchar in C - c

I am new newbie, facing a basic problem, Not able to break while loop using getchar.Following code compiles successfuly but keeps displaying "Please enter NAME" only and keeps taking typed characters from KeyBoard but doesn;t break on typing enter for new line character :-(
#include<stdio.h>
typedef struct employee
{
char name[20];
int age;
char country[20];
}emp;
void getinfo(char *str, const char *param)
{
int i;
char ch;
if (strcmp(param,"NAME" ) == 0)
{
printf("\nPlease enter NAME \n");
}
else if(strcmp(param, "COUNTRY") ==0)
{
printf("\nPlease enter COUNTRY \n");
}
while((ch==getchar())!= '\n')
{
str[i] = ch;
i++;
}
str[i]= '\0';
}
int getage(int *age)
{
printf("\n Please enter Age \n");
scanf("%d",age);
}
int main(void)
{
emp e1;
getinfo(e1.name, "NAME");
getinfo(e1.country, "COUNTRY");
getage(&e1.age);
}
Please provide help.

The
while((ch==getchar())!= '\n')
should read
while((ch=getchar())!= '\n')
^
Otherwise, you're comparing the (uninitialized) value of ch to getchar(), instead of assigning the result of getchar() to ch.

while((ch==getchar())!= '\n')
That line is currently an undefined operation since ch isn't assigned anything yet. Let's assume ch==getchar() is false, false != '\n' is true, since anything nonzero is true, and '\n' is greater than zero.
I assume you want to change the == in ch==getchar() to a single =.

You have typed while((ch==getchar())!='\n')
This statement calls getchar() and compares to the garbage value of ch, and compares against '\n'
it will be usually true, so the while loop keeps on executing even though ch is assigned nothing
So, change it to while((ch=getchar())!='\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;
}
}

How to do input validation using a struct variable array in C

I've made a small program to write Student names and final grades to a file and then read that file and print it. I'm still new to C and I'm playing with input validation, but I'm running into a problem when it comes to using it with a struct. When I run my function it checks for cases just fine, but it does not store the right input into studentFinalGrades. So when I go to print the file, it never stored the input for final grades. Any ideas on how I can fix this? Or advice on how to do input validation another way?
Here is my code:
#include <stdio.h>
#include <stdlib.h>
//struct of students
struct students
{
char name[50];
char lastName[50];
char studentFinalGrade[50];
};
//myfunctions (at bottom of main)
char check_letterGrade(void);
void clear_input_buffer(void);
int main()
{
// in check_letterGrade
char input;
if (input != '\n')
{
clear_input_buffer();
}
//dynamic array
struct students a[2],b[2];
FILE *fptr;
int i;
//opens file.txt
fptr=fopen("file.txt","wb");
//Loop to enter in all student data and grades at once (only did 5 students because it takes forever to fill it out)
for (i=0;i<2;++i)
{
fflush(stdin);
printf("Enter Student Name: ");
scanf("%s",&a[i].name);
printf("Enter Student Last Name: ");
scanf("%s",&a[i].lastName);
printf("What is students Final Grade?:");
check_letterGrade();
// scanf("%s",&a[i].studentFinalGrade);
}
//writes user input to a file and then closes file
fwrite(a,sizeof(a),1,fptr);
fclose(fptr);
//opens and reads file
fptr=fopen("file.txt","rb");
fread(b,sizeof(b),1,fptr);
//Printing all Studens names and grades entered into the file.
int j = 0;
for (i=0;i<2;++i)
{
j ++;
printf("\nStudent %d :\n",j);
printf("Name: %s\nLast Name: %s\nFinal Grade: %s\n\n", b[i].name,b[i].lastName,b[i].studentFinalGrade);
}
fclose(fptr);//close file when done reading.
}//end main
//function to check user validation on correct letter grades entered.
char check_letterGrade(void)
{
char input;
int ch;
//If input is not all of these things then it will tell user to please enter a correct letter grade
// it will then check for new line (which should clear from the buffer and allow new entry) hopefully
while (scanf("%s", &input) != 1 || (input != 'A' && input != 'B'&& input != 'C'&& input != 'D'&& input != 'F'))
{
if (input != '\n') // only take leftover input if there is leftover input
{
while ((ch = getchar()) != '\n') ;
}
printf("Please enter a correct letter grade: ");
}
return input;
}
//clears buffer
void clear_input_buffer(void)
{
int ch;
while ((ch = getchar()) != '\n' && ch != EOF);
}
You don't get studentFinalGrades because you never store it anywhere. You made a function called check_letterGrade which return a single char that contains the grade. However, when you call that function from main, you never do anything with that return value. I suspect you meant to write something like:
a[i].studentFinalGrade = check_letterGrade();
You also need to change the definition of your struct. There you define studentFinalGrade as char[50], but you probably just mean char since you only need a single letter, right?
I realize you are new to C, but your program really contains a lot of strange stuff. Actually, I'm surprised the above code even compiled for you, as scanf("%s",&a[i].name) ought to have given you a syntax error. You may want to invest in a book to learn C and work out the examples first.

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.

No output during word swap

#include <stdio.h>
#include <ctype.h> /* for access to the toupper function */
void reverse_name(char *name);
int main(void)
{
char input[100];
printf("Enter a first and last name: ");
gets(input);
reverse_name(input);
return 0;
}
void reverse_name(char *name){
char *first = name;
char *see;
see = name;
while(*see != ' '){
}
while(*see != '\n'){
putchar(*see);
}
printf(", %c",*first);
}
I want this to happen: Enter a first and last name: Lloyd Fosdick
And the output be like this: Fosdick, L.
But after entering a first and last name, Lloyd Fosdick, the program doesn't give any answer and nothing happens? Whats wrong with the code?
You neither increment see nor check for 0 terminator in both of your while loops:
while(*see && *see != ' '){
see++;
}
while(*see && *see != '\n'){
putchar(*see);
see++;
}
gets() has been removed from C11 and you should never use it even if you are following older standards. Use fgets() instead.
This is by no means a complete fix. You'll have to ask yourself:
what if the input contains more than one space between the names?
what if the input contains more than two names?
What if there's no first name or last name?
etc.
You need to think about all these cases and handle them in your code.
You have an infinite loop
while(*see != ' '){}
You need to increment your pointer position.
I think that this is better code
(gets function is deprecated)
You read format code. their is space between firstName and secondName.
No infinite loops not loops at all.
int main(void)
{
char firstName[100];
char secName[100];
printf("Enter a first and last name: ");
scanf("%s %s",firstName,secName);
printf("%s ,%c",secName,firstName[0]);
return 0;
}

Data Validation in C

I was trying out a simple program in C for validating user data. The program is supposed to identify whether a user entered character is a number, alphabet or a special character.
Somehow , the code identifies every kind of input character as a number. I have appended the code below, I'd be grateful if someone could kindly point out where I'm going wrong ?
//Program to take input from the user and determine whether it is character, number, or a special character
#include<stdio.h>
#include<conio.h>
#include<string.h>
char ch;
int main()
{
clrscr();
printf("Enter a character \n");
scanf("%c \n",ch);
if ((ch>='A'&& ch<='Z')||(ch>='a'&& ch<='z') )
{
printf("The character entered is an alphabet \n" );
}
else if ((ch>=0)&&(ch<=9))
{
printf("Character entered is an number \n");
}
else
{
printf("Character entered is a special character");
}
return 0;
}
scanf accepts a pointer as the argument for %c. In other words,
scanf("%c \n",ch);
should be written as:
scanf("%c\n",&ch);
Without the reference operator (&), scanf receives the value of ch. In this case, the value is garbage, because ch is unset.* Referencing ch gives scanf a pointer to ch, not ch itself, so scanf can modify the value of ch by dereferencing the pointer (using the dereference operator, *).
There's also the issue with digit checking that Himadri mentioned.
* This is actually undefined behaviour.
Oh, Arun very silly mistake.
In your second condition in else if you have to right 0 and 9 in single quotation mark.
So, your code will be -
if ((ch>='A'&& ch<='Z')||(ch>='a'&& ch<='z') )
{
printf("The character entered is an alphabet \n" );
}
else if ((ch>='0')&&(ch<='9'))
{
printf("Character entered is an number \n");
}
else
{
printf("Character entered is a special character");
}
May be this is the only mistake. Now, it should work.
A few comments on style:
conio.h and clrscr() are non-standard.
Global variables are bad (char ch). Declaring them non-static is also bad.
Always check the return value of scanf. This will help you catch input format errors.
In this case, as we need to just a single character, getchar is more appropriate.
This is how I would've written this program:
#include <stdio.h>
#include <ctype.h>
int main()
{
int ch; /* We use an int because it lets us check for EOF */
printf("Enter a character: ");
fflush(stdout); /* Remember to flush the output stream */
ch = getchar();
if (ch == EOF)
{
printf("end-of-file or input-error\n");
return 1;
}
if (isalpha(ch))
printf("The character entered is an alphabet\n" );
else if (isdigit(ch))
printf("Character entered is an number\n");
else
printf("Character entered is a special character\n");
return 0;
}

Resources