In C, why do I only need getchar() to remove characters sometimes? - c

I am trying to use getchar() to remove characters from the input buffer. In the following code, the user is asked to input a choice to select, and then depending on the choice, another input is required, either type int or type char (string).
In the int case, getcar() is not needed and scanf takes in input correctly. But in the char case, scanf fails to get input without using getchar() beforehand. Is there a reason why that is?
printf("Available Ciphers:\n1) Caesar Cipher\n2) Vigenere Cipher\nSelected Cipher: ");
if(scanf("%d", &choice) != 1){
printf("Error: Bad selection!\n");
exit(EXIT_SUCCESS);
} else if (choice != 1 && choice != 2){
printf("Error: Bad Selection!\n");
exit(EXIT_SUCCESS);
//If the choice entered is correct, then run the following.
} else {
if(choice == 1){
printf("Input key as nuumber: ");
if(scanf("%d", &caesarkey) != 1){ //Why is getchar() not needed here?
printf("Error: Bad Key!\n");
exit(EXIT_SUCCESS);
}
//morecode here
} else if (choice == 2){
printf("Input key as string: ");
while(getchar() != '\n'); //Why is this needed here?
/*Uses scanf and not fgets, since we do not want the
key to contain the newline character '\n'. This is
due to the fact that the newline character is not
considered in the function that encrypts and decrypts
plaintext and ciphertext.*/
if(scanf("%[^\n]s", vigencipherkey) != 1){
printf("Error, Cannot read inputted key!\n");
exit(EXIT_SUCCESS);
}
//More code here..
}
}

It seems that you are scanning for a string rather than an int, and as such, you are passing in an int rather than the address of an int.
Change this line
if(scanf("%[^\n]s", vigencipherkey) != 1){
To
if (scanf("%d", &vigencipherkey) != 1) {

In order to read the remainder of the line input by the user, you can use this function:
int flush_line(void) {
int c;
while ((c = getchar()) != EOF && c != '\n')
continue;
return c;
}
Notes:
c must be defined as int to accommodate for all values of the type unsigned char and the special negative value EOF.
you should test for '\n' and EOF otherwise you will have an endless loop on premature end of file without a trailing newline, such as would occur if you redirect the input of your program from an empty file.
you can test for end of file by comparing the return value of flush_line() with EOF.

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;
}
}

Looping Input Validation for a double type, but the user inputs "abc" as input

If the user inputs "abc" as the input, the Do-While Loop loops around 3 times, one time for each letter. However, I want it to loop only once.
int main(void) {
do {
printf("Enter how much money you can contribute: ");
numArgsRead = scanf(" %lf", &availableFundsToContribute);
scanf("%c", &finalValOnLine);
} while (!((availableFundsToContribute > 0) && (numArgsRead == 1) && (finalValOnLine == '\n')));
return 0;
}
Here's what my output looks like:
Enter how much money you can contribute: abc
Enter how much money you can contribute: Enter how much money you can contribute: Enter how much money you can contribute:
Here's what I want it to look like:
Enter how much money you can contribute: abc
Enter how much money you can contribute:
There are two main options — continuing with scanf() and switching to fgets() and sscanf().
Continuing with scanf()
As noted in comments, you need to track when scanf() fails to convert the input without encountering EOF, and deal with the extraneous input. The normal way to deal with it is to read up to and including the end of line.
static inline int gobble_to_eol(void)
{
int c;
while ((c = getchar()) != EOF && c != '\n')
;
return c;
}
int main(void)
{
int numArgsRead;
double availableFundsToContribute = 0.0;
char finalValueOnLine = '\0';
do
{
printf("Enter how much money you can contribute: ");
if ((numArgsRead = scanf("%lf", &availableFundsToContribute)) == EOF)
break;
if (numArgsRead == 0)
{
if (gobble_to_eol() == EOF)
break;
}
else
{
if (scanf("%c", &finalValOnLine) == EOF)
break;
if (c != '\n')
{
/* Input might be 3.14abc */
if (gobble_to_eol() == EOF)
break;
}
} while (!(numArgsRead == 1 && availableFundsToContribute > 0.0 && finalValOnLine == '\n'));
return 0;
}
Point of detail: all formats except three (%c, %[…] (scan sets), and %n) skip leading white space, including newlines, automatically. There is no need include the blank in "%lf" (but there is also no harm in doing so at the start, but putting a blank at the end is a UI disaster).
Switching to fgets() and sscanf()
An alternative approach reads whole lines and then scans them with sscanf(). This was mentioned by user3386109 in a comment, but it is standard advice and often the best way to deal with the input.
The code might look like this:
int main(void)
{
char buffer[4096]; // Make it bigger if you prefer!
double availableFundsToContribute = 0.0;
char finalValOnLine;
do
{
printf("Enter how much money you can contribute: ");
if (fgets(buffer, sizeof(buffer), stdin) == NULL)
break;
if ((numArgsRead = sscanf(buffer, "%lf%c", &availableFundsToContribute, &finalValOnLine)) != 2)
{
buffer[strcspn(buffer, "\n")] = '\0'; // Zap newline
fprintf(stderr, "Unrecognized input [%s] - try again.\n", buffer);
}
} while (!(numArgsRead == 2 && availableFundsToContribute > 0 && finalValOnLine == '\n'));
return 0;
}
In both cases, I would probably use a top-controlled loop (for or while) but I'm keeping close to the original code. There probably should be error reporting associated with the call to gobble_to_eol() in the first code, so that the user knows what the program thinks is wrong. Note that using scanf() directly makes it hard to report on what caused the problem; reading a line and then parsing it means you can report the erroneous input accurately. Note, too, that using fgets() means that empty lines will be reported as an error, rather than silently ignored waiting for new input.
Warning: no compiler was consulted about the accuracy of the code. Treat with due caution.

While GetChar != EOF terminating inconsistently

I have these two blocks of code, the aim of both of which is to get some user inputs, to make a cryptography script.
int keyPrompt(){
/* this prompts the user for the Caesar encryption key */
int c;
printf("Enter key value between 1-9: ");
while((c=getchar()) != EOF && c != '\n'){
return c;
}
}
int cryptPrompt(){
/* this asks the user whether they want to encrypt or decrypt */
int d;
printf("Do you want to encrypt or decrypt?(E/D): ");
while((d=getchar()) != EOF && d != '\n'){
/*
if(d == 'E'){
return 1;
}
else if (d == 'D' ){
return -1;
}
*/
return d;
}
}
The problem I'm having is that when I run the file, the first while loop behaves as I expect it to: I enter a value, hit enter and it goes to the next line. With the second while loop, when it executes the function, it skips asking the user for an input and simply goes straight to the next line of good, not storing any return value.
Any idea why it's doing this?
Consider what this code does:
int keyPrompt(){
/* this prompts the user for the Caesar encryption key */
int c;
printf("Enter key value between 1-9: ");
while((c=getchar()) != EOF && c != '\n'){
return c;
}
}
After printing the prompt, it reads an input character. If that character is an EOF or a newline, it exits the loop and falls off the end of the function (without a return, which leads to undefined behavior if the returned value is not ignored). If its NOT an EOF or newline, it returns the character.
In no case will the while loop ever actually loop.
Consider what happens if you call this code and enter a key+newline. The key will be read and returned, and the newline will be left in the input buffer. Now consider what happens if you call another function like this -- the first thing it will read is the newline left over from the keyPrompt function...

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.

when scanf returns 0 in c and just doesn't work

'when there is no successful assignments' i know that scanf returns 0 to indicate it, but is that the only thing that it does? this is my code:
#include<stdio.h>
int main(void) {
int val,x;
x=scanf("%d",&val);
if(x==1)
printf("success!");
else{
printf("try again\n");
scanf("%d",&val);
}
return 0;
}
if i enter a number, it works fine but if i enter a character scanf doesn't work anymore, this is what i get:
k
try again
process returned 0 (0x0) execution time :2.578 s
press any key to continue.
_
meaning that it doesn't allow me to enter a new value, why is that? is there something wrong in the code? if yes how can i fix it? should i stop using scanf?
When scanf doesn't work, the invalid data is still left in the stream. You'll have to read and discard the data from the stream first before you can enter more data.
#include<stdio.h>
int main(void) {
int val,x;
x=scanf("%d",&val);
if(x==1)
printf("success!");
else{
// Discard everything upto and including the newline.
while ( (x = getchar()) != EOF && x != '\n' );
printf("try again\n");
scanf("%d",&val);
}
return 0;
}
The scanf family of functions are broken-as-specified and should never be used for anything.
The correct way to write this program is to use getline, if available, or fgets otherwise, to read an entire line of user input. Then use strtol to convert the input to a machine integer, taking care to check for errors:
errno = 0;
result = strtol(line, &endptr, 10);
if (endptr == line || *endptr != '\n' || errno)
// invalid input

Resources