Hi please take a look on this code:
while (cont == 1) {
...
scanf_s("%d", &input);
if (0 < input <= 5){
switch (input) {
case 1:
printf("1");
break;
case 2:
printf("2");
break;
case 3:
printf("3");
break;
case 4:
printf("4");
break;
case 5:
cont = 0;
break;
default:
printf("Wrong input !");
break;
}
}else{
printf("Error, Not a number !");
}
}
If I input something that is not a number, it results in an infinite loop. How do I restrict char inputs?
You can use this:
if(scanf_s("%d", &input) != 1) {
printf("Wrong input !");
break;
}
You should ALWAYS check the return value of scanf_s anyway.
After the scanf_s() fails, you need to read at least one character (the character that it failed on); usually, it makes most sense to discard the rest of the line that the user entered:
while (cont == 1) {
int rc;
...
if ((rc = scanf_s("%d", &input)) < 0)
{
printf("EOF detected\n");
break;
}
if (rc == 0)
{
int c;
while ((c = getchar()) != EOF && c != '\n')
;
printf("Error, Not a number!\n");
continue;
}
if (0 < input <= 5){
switch (input) {
case 1:
case 2:
case 3:
case 4:
printf("%d", input);
break;
case 5:
cont = 0;
break;
default:
printf("Wrong input (1-5 required)!\n");
break;
}
}
}
If EOF is detected in the 'gobble' loop, you could detect EOF there and repeat the print and break the loop immediately. OTOH, the next scanf_s() should also report EOF, so it isn't 100% necessary. It depends a little on where the prompting occurs; if you get EOF, you probably shouldn't prompt again, so maybe the test after the inner while loop should be:
if (c == EOF)
{
printf("EOF detected\n");
break;
}
else
{
printf("Error, not a number\n");
continue;
}
You can play with variants of the 'gobble' loop that read up to a newline or a digit, and use ungetch(c, stdin); to return the digit to the input stream for the next call to scanf_s() to process — you probably wouldn't prompt for more input if you're going to process the already entered digit (that would confuse).
There are endless other games you can play. One option to consider is limiting the number of failed inputs before you give up — if the user hasn't entered a valid number in 10 tries, they probably aren't going to.
Note how the error processing tells the user what the valid range of numbers is; that helps them get it right. Also notice that the messages have a newline at the end; that's generally a good idea. In contexts outside interactive I/O, the newline can help ensure that the output appears when it is printed, not some arbitrary time later when some other print adds a newline, or the output buffer fills up and the pending data is flushed after all.
Related
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 ...
I am kind of a beginner and have a little problem when trying to loop a menu.
Let's take this for example:
int option = 0;
do {
printf("Menu\n");
printf("[1] Insert\n");
printf("[2] List\n");
printf("[0] Exit\n");
printf("\nOption: ");
scanf("%i",&option);
switch(option)
{
case 1: {
// code
break; }
case 2: {
// code
break; }
default: {
// error
}
}
} while(option != 0);
This will work for numbers, yet, as the option is an integer, if the user types a text, such as "a" or "owkefok" or even "ewf432k", the program will just break;
How do I give an error such as if(containstcharacters) then(error) while repeating the menu? Is it possible without changing the option to char?
You can check for return value of scanf(). Like this :
int num;
if(scanf("%d%c", &num) != 1)
printf("failure\n");
else
printf("valid integer followed by enter key\n");
On success, the function returns the number of items successfully read. This count can match the expected number of readings or fewer, even zero, if a matching failure happens. In the case of an input failure before any data could be successfully read, EOF is returned.
In simple words, if it can read your user input it will return the number of item it could read successfully.
So in your case the code will be :
int option = 0;
do {
printf("Menu\n");
printf("[1] Insert\n");
printf("[2] List\n");
printf("[0] Exit\n");
printf("\nOption: ");
scanf("%i",&option);
int check = scanf("%d%c", &num);
if(check!=1) {
//error
}else{
switch(option)
{
case 1: {
// code
break; }
case 2: {
// code
break; }
default: {
// error
}
}
} while(option != 0);
}
Instead of
scanf("%i",&option);
use e.g.
char str_option[100];
scanf("%s", &str_option);
so you will read into a string instead to an int. Then you may extract the first character of it and convert it to its numerical value (in spite it doesn't need be a digit) by the well-known trick
option = str_option[0] - '0' // Converts '0' to 0, '1' to 1, and so on
Then you need not change the rest of your code except for inserting in your switch statement:
case 0 : break; // To not fall into "default:" for error-handling
(By the way you need not use { } in the individual cases of the switch statement as all commands are executed regardless of them until they meet break; .)
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.