In the following piece of code what is while loop doing (marked with "loop")?:-
int main(void)
{
char code;
for (;;)
{
printf("Enter operation code: ");
scanf(" %c", &code);
while (getchar() != '\n') // loop
;
switch (code)
{
case 'i':
insert();
break;
case 's':
search();
break;
case 'u':
update();
break;
case 'p':
print();
break;
case 'q':
return 0;
default:
printf("Illegal code\n");
}
printf("\n");
}
}
Diclaimer:The code is not complete, it's just a part of the code because of which it won't compile.
getchar() used here to eat up the extra characters entered by user and the newline character \n.
Suppose a user entered the operation code as
isupq\n // '\n' is for "Enter" button
then, scanf() would read only character i and rest of the five characters would become consumed by the statement
while (getchar() != '\n')
;
Thus for next iteration scanf() will wait for user to input a character instead of reading it from the input buffer.
while (getchar() != '\n') // loop
;
is here to clean the buffer.
The problem that this while solves is that scanf(" %c", &code); only grabs a single character from the input buffer. This would be fine, except that there's still a newline left in the input buffer that resulted from hitting 'enter' after your input. a buffer clear is needed for the input buffer. that's what the while loop does
it is a common problem with c
Here application waits for the user to press enter.
Since the for loop in the given code is a infinite looping so the while loop is checking whether the input character is \n or not. In case the the character entered is \n it then moves toward the switch case. In general sense it is waiting of return key to be pressed to confirm your input.
if you use the fgetc function you don't have to worry and check for the enter key in your infinite loop. Even if you enter more than one character it will only take the first one
int main(void)
{
char code;
for (;;)
{
printf("Enter operation code: ");
code = fgetc(stdin);
switch (code)
{
case 'i':
insert();
break;
case 's':
search();
break;
case 'u':
update();
break;
case 'p':
print();
break;
case 'q':
return 0;
default:
printf("Illegal code\n");
}
printf("\n");
}
}
scanf() usually is not a good way to scan char variables, because the Enter you press after entering the character remains in input buffer. Next time you call scanf("%c", &input) this Enter already present in buffer gets read & assigned to input, thus skipping next input from user.
Related
So I've tried so much but I can't input a string even using: fgets, gets, scanf, and scanf("%[^\n]%*c",pharse). I need a string with the spaces. It just jumps the code line of input I think.
Please answer with a explanation of why it doesn't work
https://repl.it/#YashKumar11/String#main.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
const int DIMMAX=100;
char pharse[DIMMAX+1];
int stringLength;
int choice=0;
while(choice != '5'){
printf("1)Enter a new pharse.");
printf("\n2)");
printf("\n3)");
printf("\n4)");
printf("\n5)\n");
scanf("%d",&choice);
switch(choice){
case 1:
printf("\n=====================\n");
scanf ("%[^\n]%*c",pharse); //<-----------------------It jumps here
printf("\n=====================\n");
stringLength = strlen(pharse);
printf("%s",pharse);
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
default:
printf("\nNot a valid option.\n\n");
break;
}
}
return 0;
}
the statement to input the parase fails because the input for choice leaves a \n in the input stream.
When the second call to scanf() is made, it immediately returns (with a returned value of 0) because the first character input is \n.
suggest following each call to scanf() with:
int ch;
while( (ch = getchar()) != '\n' && ch != EOF );
that also implies that the format string of the second call to scanf() should have the %*c removed.
Your problem is not in the line that you try to read the string, but in the previous call to scanf()
scanf() was written to scanf formatted input. Keyboard input is not that. It can be everything except formatted. The user has over 100 keys to choose from
When the user types a '1' to input a phrase scanf() does not consume the newline. In fact the user can type 1 here we go to enter some text!
and then ENTER. And scanf() will be ok with just the '1'. The rest of the chars would be left there for the program to read. scanf() has no way to know what is left there.
Also scanf() return an int with the number of values read, and it can be zero if the user entered no digits. And you did not tested in your code.
Compare with your code a bit modified below
#define DIMMAX 100
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char phrase[DIMMAX + 1];
int stringLength;
printf("1) Enter a new phrase");
printf("\n2)");
printf("\n3)");
printf("\n4)");
printf("\n5) Exit\n\nOption: ");
fgets(phrase, DIMMAX, stdin);
while (phrase[0] != '5')
{
switch (phrase[0]) {
case '1':
printf("\n=====================\n");
fgets(phrase, DIMMAX, stdin);
printf("\n=====================\n");
stringLength = strlen(phrase);
phrase[strlen(phrase) - 1] = 0; // deletes the '\n'
printf("Phrase: '%s', len = %zd\n\n", phrase, strlen(phrase));
break;
case '2':
break;
case '3':
break;
case '4':
break;
case '5':
break;
default:
printf("\n'%c' (dec %d) is not a valid option.\n\n",
phrase[0], phrase[0]);
break;
}
printf("1) Enter a new phrase");
printf("\n2)");
printf("\n3)");
printf("\n4)");
printf("\n5) Exit\n\nOption: ");
fgets(phrase, DIMMAX, stdin);
}; // while()
return 0;
}
Maybe it helps to understand.
Note that instead of stopping rigth at the digit, like scanf(), fgets() read up to and including the newline, so if you are using printf() and not puts() to output it, you must take the last byte off the string read
I am trying to create a program, in which at the beginning it shows a menu to the user, which consists in a do{ ... }while; which reads an int., with a switch inside.
It works perfectly to read and check the integer, the problem is when writing a character or string, which gets stuck in an infinite loop showing the default message of the switch loop. The code is as follows:
int op;
printf("Choose an option:\n 1. option 1\n 2. option 2\n 3. option 3\n");
do{
scanf("%d", &op);
switch(op){
case 1: (instruction); break;
case 2: (instruction); break;
case 3: (instruction); break;
default: printf("\nPlease enter a valid option\n");
}
}while(op<1 || op>3);
It works perfectly to read and check the integer, the problem is when writing a character or string, which gets stuck in an infinite loop showing the default message of the switch loop.
scanf("%d", &op); does nothing when the input is not a valid int, you need to check the return value is 1 and if not to decide what to do like for instance read a string or flush up to the end of line, also managing the EOF case
Note in case scanf does nothing op is not set
So can be :
int op;
printf("Choose an option:\n 1. option 1\n 2. option 2\n 3. option 3\n");
do{
if (scanf("%d", &op) != 1) {
// flush all the line
while ((op = getchar()) != '\n') {
if (c == EOF) {
puts("EOF, abort");
exit(0); /* what you want */
}
}
op = -1; /* invalid value */
}
switch(op){
case 1: (instruction); break;
case 2: (instruction); break;
case 3: (instruction); break;
default: puts("\nPlease enter a valid option");
}
}
}while(op<1 || op>3);
I encourage you to never trust on an input and always check return value of scanfetc
The %d conversion specifier is seeking for decimal input only. It does not work to consume characters or strings. If you input a character instead of an decimal value, the directive will fail, and because op isn´t initialized you have undefined behavior.
To catch a string with scanf() use the %s conversion specifier or use fgets(). For only catching one character use the %c conversion specifier with scanf().
int DisplaySchedule()
{
int nDisplaySchedule_Choice;
system("cls");
printf("----- DISPLAY SCHEDULE -----\n");
printf("Pick departure station\n\t");
printf("[1] San Pedro\n\t");
printf("[2] Santa Rosa\n\t");
printf("[3] Calamba\n\n\t");
printf("[9] Go Back\n\t");
printf("[0] Exit\n\n");
printf("Choice: ");
scanf("%d", &nDisplaySchedule_Choice);
printf("\n");
switch (nDisplaySchedule_Choice) {
case 1: SanPedro(); break;
case 2: SantaRosa(); break;
case 3: Calamba(); break;
case 9: OpeningScreen(); break;
case 0: printf("Summary()"); break;
default:
printf("ERROR. INPUT A VALID RESPONSE.\n\n");
DisplaySchedule();
break;
}
return;
}
I have this code in which when I enter a letter, instead of printing the error message, it prints case 0: instead. Is there any way for me to make it so that case 0: will only function if and only if I enter "0" in the scanf statement?
You have undefined behaviour here.
scanf, when scanning for int (%d), fails because you input a character - due to matching failure. Thus not reading anything into nDisplaySchedule_Choice at all.
Since nDisplaySchedule_Choice is uninitialized to start with, it just happens to have 0 and thus goes to case 0.
The solution is to check the scanf return value before proceeding to use nDisplaySchedule_Choice. scanf returns the number of items successfully scanned.
If scanf fails to read a value (for instance because you told it to read an int and gave it a letter) it won't change your variable. So nDisplaySchedule_Choice won't change in a way that you can check in your switch. At least not if you don't initialize it - you can however set it to a value that is not covered by your switch, and if it didn't change, you know that scanf failed to read a value.
Or you could check the return value of scanf to see if it managed to read a value:
int result = scanf("%d", &nDisplaySchedule_Choice);
if (result == 0) {
int c;
while ((c = getchar()) != '\n' && c != EOF); // flush the invalid input
printf("ERROR. INPUT A VALID RESPONSE.\n\n");
DisplaySchedule();
}
else switch ...
UPDATED CODE
Why does the input 12 works? It interprets 12 as option 1 and takes 2 for the first scanf of case'1'? I do not want to use int opcao as if I enter a leter it will run indefinitly.
I want that the user can only exit the program when he chooses option exit (one of the cases) thus the do... while.If the user enters an invalid character or leter or whatever the menu shows again and shows the default message. I also want that after the chosen case is executed, it presents the menu again for a new choice thus i am using an always valid condition of 1=1 on the while. I can't use integers as if you enter a leter the program goes bonkers a.k.a never stops running. just try it.
char opcao;
do {
menu();
scanf(" %c",&opcao);
switch(opcao) {
case '1':
printf("Massa do módulo (sem combustível):\n");
scanf("%f",&m_modulo);
printf("Massa de combustível:\n");
scanf("%f",&m_combustivel);
printf("Altitude no início da alunagem em relação a um ponto de referência:\n");
break;
case '2':
break;
case '3':
printf("Funcionalidade nao disponivel.\n");
break;
case '4':
printf("Funcionalidade nao disponivel.\n");
break;
case '5':
printf("Funcionalidade nao disponivel.\n");
break;
case '6':
exit(0);
break;
default:
printf("Opcao invalida, as seguintes opcoes estao disponiveis:\n");
break;
}
}
while(1==1);
That's because you're reading your input with a single %c.
This way, the first character of 12 (1) is used by the switch, while the second is used by the scanf of case '1':.
To avoid this behaviour, you can read the options as integers and use the placeholder %d in your very first scanf.
EDIT:
To avoid your infinte loop problem, you can do like this:
#include <stdio.h>
void clean_stdin();
int main() {
int opcao;
float m_modulo, m_combustivel;
int flag = 0;
do {
printf("Make a choice: ");
if (scanf("%d", &opcao) == 0) {
clean_stdin();
}
else {
switch(opcao) {
case 1:
printf("Massa do módulo (sem combustível): ");
scanf("%f", &m_modulo);
printf("Massa de combustível: ");
scanf("%f", &m_combustivel);
printf("Altitude no início da alunagem em relação a um ponto de referência.\n");
break;
case 2:
break;
case 3:
printf("Funcionalidade nao disponivel.\n");
break;
case 4:
printf("Funcionalidade nao disponivel.\n");
break;
case 5:
printf("Funcionalidade nao disponivel.\n");
break;
case 6:
flag = 1;
break;
default:
printf("Opcao invalida, as seguintes opcoes estao disponiveis:\n");
}
}
} while(flag == 0);
}
void clean_stdin()
{
int c;
do {
c = getchar();
} while (c != '\n' && c != EOF);
}
What I've done is the following:
Check the scanf output if it reads the input correctly (in this specific case, if the input was a number it returned a number different from 0).
Use the function clean_stdin (Credits) to clean characters that scanf read but didn't consumed (it expects a number and you give it a character, so the character stays in stdin and create the infinite loop)
I use a flag to control the loop condition, when the exit option is chosen, flag value is changed to a value that makes the condition fail
I added the main() because I need it to run the program; you can incorporate what's inside in your main. Remember to copy clean_stdin() function.
I suggest you to read some scanf documentation to understand its return value.
I suggest also to read about scanf alternatives, since it's a boring function: link 1 and link2.
Remember to format your code with the right indentation, it's a best practice.
That's how scanf works.
You asked scanf to read a single character from the input stream. The input stream originally contained 12 sequence (more likely, 12<newline> sequence). So, just like you asked it to, scanf consumed that first 1, leaving the rest in the input stream.
The next scanf continued to consume the input stream where the previous one left off.
scanf with the %c can read one character at once. '12' contains two characters '1' and '2'. So '1' will be consumed firstly by the scanf and hence,case '1': gets executed. The '2' is left in the input buffer(stdin) and it will be consumed by the next scanf with a %c.
To avoid this,you could declare opcao as an integer and use the following code:
while(1)
{
if(scanf("%d",&opcao)==0)
{
printf("Invalid input. Try again:");
scanf("%*s"); //remove the invalid input
continue;
}
if(opcao>0 && opcao<7)
break;
else
printf("invalid integer. Try again:");
}
switch(opcao) {
case 1://your code
break;
case 2://your code
break;
case 3://your code
break;
// etc...
case 6:exit(0);
}
//no need of do...while or a default case
I have this application where it reads user input with fgets. It works fine and have no problem if I take user input while program is running. But if I feed a file say "./myprog < in.txt", it goes into infinite loop and print my custom error message that I show when user doesn't provide valid input all over the screen. It's like return key is pressed. How do I solve that problem?
This is one section of my program that's has fgets
while(1){
mainMenu(&choice);
switch(choice)
{
case 0:
return 0;
case 1: //Add movies
getMovieData(&list);
break;
case 2:
....some initialization...
printf("Provide a name to delete : ");
fgets(inputBuffer,sizeof(inputBuffer),stdin);
trimWhiteSpace(inputBuffer);
deleteMovie(&list,inputBuffer);
break;
case 3: //List all movies
printMovieData(list);
break;
case 4: //List movies by genre
inputBuffer[0] = '\0';
printf(".....");
fgets(inputBuffer,sizeof(inputBuffer),stdin);
trimWhiteSpace(inputBuffer);
if(!isNum(inputBuffer,&genreChoice)){
showMessage("Number Expected\n");
break;
}
else if(!checkNumberValidity(&genreChoice,1,6)){
showMessage("Choose from 1 to 7\n");
break;
}
else printMoviesByGenre(list,getGenre(genreChoice-1));
break;
}
}
while(1){
printf("Provide the number of movies you would like to enter: ");
fgets(temp, sizeof(temp), stdin);
trimWhiteSpace(temp);
if(!isNum(temp,&numMovie)) printf("Enter a numeric value\n");
else if(!checkNumberValidity(&numMovie,1,MAX_MOVIES)) printf("No greater than %d\n",MAX_MOVIES);
else break;
}
Check the return value of fgets. It returns NULL on failure. At the end of the stream, fgets return NULL and feof(file) will be != 0, indicating you reached the end of stream (EOF, end of file).
As Charlie Burns said, you must exit on end of input, so do something like this:
if (fgets(temp, sizeof(temp), stdin) == NULL)
exit(EXIT_SUCCESS);
... in your while(1) loop.
Related: fgets and dealing with CTRL+D input
i don't think the infinite loop comes from fgets. But its not well declared fgets(inputBuffer,sizeof(inputBuffer),stdin); second argument must be a size, size that says how much char you wanna read. Show us where is you printf that you see during the infinite loop.