I'm just starting to study C. I have a program that prints a menu and let users choose what to do step by step. Now I would like to return to the main menu whenever the user enters an empty line, but how can I do that?
I think I can make a function that return the program to the main menu, but when to call that function? I know it's not good to put an if-else whenever I scanf something...
I am used to the OO world, so this is a bit unfamiliar to me, please help :)
I'm guessing you're using a switch statement to filter your input, so just make an option for '\n', or the newline character, to output the print the menu.
If you're doing something along the lines of:
printf("0) do something\n");
printf("1) do something else\n");
printf("enter) main menu\n");
...then scanf isn't really your friend.
You could do something like this:
char buf[80];
int choice;
printf(menu_text);
fgets(buf, 80, stdin);
if(strlen(buf))
{
sscanf(buf, "%d", &choice);
switch(choice)
{
case 0:
/* etc */
break;
case 1:
/* etc */
break;
}
}
else
{
go_back_to_main_menu();
}
It depends on if the user is communicating by sending a string or a single character/keypress.
If communicating by string, try starting with:
char buffer[MAX_BUF_LEN];
char* pBuffer = buffer;
scanf("%s%*c",pBuffer);
if (strlen(pBuffer) == 0)
goto_main_menu();
else
process_user_input(pBuffer);
If communicating by character/keystroke, try starting with:
int inkey = getchar();
if (inkey == '\n')
goto_main_menu();
else
process_user_input(inkey);
Using an "if/else" after you scanf something is perfectly valid. Anything sent from the user should be checked and validated before it is used anyway.
C language is not an OO world, so I'd say stick with if-else. Anyway, when creating text menus you usually end up with switch(user_choice) of if-else.
Do you need a help with scanf()?
Related
I'm programming this code (personal software) for a spa which is still in a beta version but I encounter a issue well is more like an idea than an issues let me explain you:
Source code:
fflush(stdin);
gets(NC1.Customer_Nameandlastname);
fflush(stdin);
printf("provide the customer's age\n\t");
fflush(stdin);
scanf("%d",&NC1.Customer_Age);
fflush(stdin);
this is just part of the source code but what I want to do when the program is running is this:
If the the person that is typing the information makes a mistake or wants to retype the same information but the next command line is already waiting for the input data the question is how would I do to go back to the previous line and then after I finish continue to the next line?
So it is like how would I return to the previous scanf() if I already type that information and then the system is waiting for the next line.
Please help me because I dont really know what yo do I am seeking how to do it but I still not able to find it.
You cannot portably flush input from stdin with fflush(stdin);. It invokes undefined behavior.
You can read the rest of an offending line from stdin with this function:
void flush_line(FILE *fp) {
int c;
while ((c = getc(fp)) != EOF && c != '\n')
continue;
}
Furthermore, do not use gets(). This unsafe function (you cannot provide the size of the destination array, so any properly crafted input may cause undefined behavior, a flaw that can be used by an attacker to compromise your program). The function was finally removed from the C Standard in 2011. Use fgets() and remove the trailing linefeed if any this way:
if (fgets(line, sizeof line, stdin)) {
line[strcspn(line, "\n")] = '\0';
...
}
You cannot restart a failed scanf(). The return value gives you some information as to where it failed, but not enough to restart the parse reliably. A better way to parse standard input is to read line by line and use sscanf() to parse the lines. Example:
/* read the customer's age. Keep prompting in case of invalid input */
for (;;) {
char line[80];
printf("provide the customer's age\n\t");
/* force flush of stdout for systems that do not do it
automatically upon reading from stdin */
fflush(stdout);
if (!fgets(line, sizeof line, stdin)) {
/* end of file reached */
return -1;
}
if (sscanf(line, "%d", &NC1.Customer_Age) == 1) {
/* input parsed correctly, break out to the next question */
break;
}
/* parse failed, output an appropriate message and restart */
printf("bad input, please type a number\n");
}
This is really a basic problem, what you want is a loop like this
while (fgets(buffer, SIZE_OF_BUFFER, stdin) != NULL) {
if (is_input_valid(buffer) == 1)
break;
/* Or else, go again */
}
After asking the user for all the details, ask something like
Do you want to store this record in the database?
Instruct the user that if he makes a mistake, he should enter garbage for the following fields, and answer "no, I want to discard this record".
Maybe the user will understand by himself that this is what he should do, because asking for such confirmation is a pretty standard practice.
I am fairly new to programming and i have done some in school, but they only taught basic functions like printf, scanf, for, while, pointers. I am making a small program that will print user input in to a file, but when the text is written into the file the first letter of every word after the first is missing and i don't know why. can somebody explain what is happening to the first letters and do so in a simple manner if you can, please and thank you for your answer.
This is the function i am using to write to the file.
void text(){
int e=1;
puts("After a sentance press enter to continue or esc to stop");`
printf("Enter text now\n");
FILE *fp;
fp=fopen("Text.txt","w");
End:while(e==1){
char txt[100];
puts(gets(txt),fp);
if(getche()=='\e')
e=0;
goto End;
} //end of while
fclose(fp);
}// end of function
New code
void text(){
int e=1;
puts("After ending a sentance press enter to continue or esc to stop");
printf("Enter text now\n");
FILE *fp;
fp=fopen("Text.txt","w");
while(e==1){
char txt[100];
fgets(txt,100,stdin);
fwrite(txt,sizeof(char),sizeof(txt),fp);
if(getche()=='\e'){
break;
}
else;
}
fclose(fp);
}
Your code has many issues, but... your particular problem is that getche() reads one character, if it is \e you do something, but if it is another characters, then it is lost!
Using your functions of choice (getche() and gets()) you can do:
while (1)
{
char c = getche();
if (c == '\e')
break;
txt[0] = c;
gets(txt+1);
/* ... */
}
This will not remove the first character, but will fail with empty lines, I think.. I will leave that as an exercise to the reader.
I am a beginner in C programming and I'm trying to do basic stuff. I'm trying to create a simple menu using a switch statement like this:
int disp(){
int check;
while(check!=1){
int choose;
printf("1 \n");
printf("5quit\n");
scanf("%d", &choose);
switch(choose)
{
case 1:func1();
break;
case 5: check = 1;
break;
default:
printf("wrong input");
}
return 0;
}
}
However, when either default or case 1 happen, I want to make it still loop the menu until a good input is made. For example, if I enter random stuff like "asdf", the program should say "wrong input" and then display the menu again and wait for user input. And if user enters "1", the program will run func1 and then go back to the menu and wait for the user to input a choice.
Currently, when a wrong input happens it just shuts down the program instead of displaying the menu again, and I don't know how to solve that.
Your return 0 statement is inside of your while loop. Move it down one line, outside of the loop.
This is one reason why proper indentation is important.
At this point, entering in non-numeric input results in an infinite loop because scanf isn't reading in anything. To remedy this, you call getchar in a loop after calling scanf to flush the buffer:
int c;
scanf(" %d", &choose);
while ((c=getchar()) != EOF && c != '\n');
This is correct and close to what you want
// Use a proper formatting standard to improve readability
int disp() {
int check = 0; // Initialize the loop variable
while (check != 1) {
int choose;
printf("1 \n");
printf("5quit\n");
scanf("%d\n", &choose); // Ask the scanf to swallow the newline
switch(choose) {
case 1:
func1();
break;
case 5:
check = 1;
break;
default:
printf("wrong input");
}
}
return 0; // Pull the statement out of the loop
}
EDIT: Since you are providing invalid input intentionally, it's better to check the return value of scanf to avoid errors.
I can't seem to figure out why my program doesn't work if I call scanf() from within a case statement.
Everything works fine until I call scanf(). It's a really small console based menu. It reads a string that has just been entered and goes down the menu accordingly. It all works fine until I try scanf() in an integer that it should respond to.
I first thought that returning a value will break the Switch statement... then that didn't work so I placed break statements in just in case. But it still doesn't work though. As soon as the scanf() is not commented the whole sequence is just messed up.
void main(void) {
char MenuS;
MenuS = 's';
while (MenuS != 'q') {
MenuS = Menu();
}
}
char Menu() {
unsigned char Menu;
unsigned char UserInput[9];
unsigned int Offset;
Offset = 0;
printf("\nEnt:");
gets(UserInput);
trim(UserInput);
if (UserInput[0] != 'q') {
switch (UserInput[0]) {
case 'r':
scanf("%d", &Offset);
printf("\nIn R");
return ('r');
break;
case 'w':
// printf("Is w");
printf("\nIn W");
return ('w');
break;
default:
printf("\nSomething else");
break;
}
} else {
return 'q';
}
}
I think you're having a problem, because you declared an unsigned int variable, but you used a wrong format specifier here:
scanf("%d", &Offset);
Offset is an unsigned int variable.
You have to use %u for unsigned integer instead of %d
Try this, this might solve the problem:
scanf("%u", &Offset);
Learn to enable all warnings and debug info in your compiler. With GCC compile using gcc -Wall -g
Then use the debugger (e.g. gdb) to run your program step by step.
At last, read the documentation of functions that you are using. scanf(3) is documented to return an integer number:
These functions return the number of input items successfully matched
and assigned, which can be fewer than provided for, or even zero in
the event of an early matching failure.
The value EOF is returned if the end of input is reached before
either the first successful conversion or a matching failure occurs.
EOF is also returned if a read error occurs, in which case the error
indicator for the stream (see ferror(3)) is set, and errno is set
indicate the error.
So you should code probably
if (scanf("%d", &Offset)<1) { perror("scanf failed"); exit (EXIT_FAILURE); };
BTW, your printf(3) format strings look strange: they should end (not start) with a newline \n e.g. code printf("In R\n"); or even better
printf("line %d of %s: In R\n", __LINE__, __FILE__);
or at least call explicitly fflush(NULL); after them, read
about fflush(3) and remember that stdio(3) is buffered.
At last, gets(3) is deprecated and dangerous, use fgets(3) or preferably getline(3). So at least replace gets(UserInput); with
memset (UserInput, 0, sizeof(UserInput));
if (NULL == fgets(UserInput, sizeof(UserInput), stdin))
{ perror("fgets"); exit(EXIT_FAILURE); }
BTW, your main function is incorrectly declared. It should be int main (int argc, char**argv); preferably. It should not return void but EXIT_SUCCESS i.e. 0 on success and something else on failure. See also EXIT_SUCCESS and EXIT_FAILURE and exit(3)
PS. You are missing several #include directives at the beginning of your program, at least:
#include <stdio.h>
#include <stdlib.h>
I assume your code have them but you did not show them to us.
NB: Using capital letters in variables is not conventional in C. Generally, people
reserve upper-case letters for macros. But that is a convention.
when I call ScanF from within the Switch it seems to ignore the Return AND the Break and just to Default which just prints "Not Something"..
If it "ignored the return and the break" then you would fall through to the case 'w'. But that isn't happening. Therefore it is not ignoring the return and the break (not that that is possible anyway).
Actually the program flow is working just as you wrote it. The sequence is:
scanf("%d", &Offset);
You type some numbers and press enter
return ('r');
back to main: MenuS = Menu(); - MenuS is now 'r'
while (MenuS != 'q') { MenuS = Menu(); - call Menu again
gets(UserInput); - gets the blank string you typed earlier
if (UserInput[0] != 'q') - true, since UserInput[0] is 0, the end-of-string marker for your blank string
switch (UserInput[0]) { there is no case 0, so default: is entered
printf("\nSomething else");
To fix this: you need to discard the Enter that was pressed after you typed in the number. One way is to put this code straight after the scanf:
int c;
while ( (c = getchar()) != '\n' && c != EOF ) {}
Note, your code has a lot of other problems but this was the main issue.
I have a simple program, intended simply to excercise my freshly gained knowledge of C.
Please keep in mind I have attempted to solve the problem of the newline leftovers remaining in the stdin buffer using the knowledge I already have. From the problems I was having before I figured out the newline issue, I have made considerable progress in getting this program to act the way I imagine it should. This is all I have left to debug and I am stymied. While my fluency in C is marginal, at best, I believe this program can easily work without a whole lot of modification.
The program asks the user to select a tool, of 2 simple tools I have constructed. The tools themselves work fine, but the cheesy 'pick-a-letter' UI that I've built has a bug I just can't figure out. It runs through the program as expected the first time, but the second time, the variable used for tool selection gets switched to a newline character, and the user input assignment for that variable gets overwritten. Therefore the program enters the switch construct used for the tool selection variable and activates:
case '\n':
which prints an error message indicating a newline got in the way, and breaks out to return to the beginning of the menu loop. On this third time around, the program will again work as expected. It will continue to alternate like this until you exit the program via the exit provided at the end of a normal program run.
This is a direct cut and paste of pertainant code, not included are the D_Time(); and cal(); functions. Feel free to compile and run with substitue functions to see the problem firsthand if need be. Both are void functions, one is a simple 2 argument calculator and the other displays date and time via stdout. And any other suggestions you have that could improve my knowledge of C or this program would be happily accepted and are sought. I am still learning, and although pointers and calculus escape me, i will get the hang of this.
void clrnl(void)
{
while (getc(stdin) != '\n');
} /* This is the fix that kind-of but not-really fixes the '\n' problem. Without it, the error message would pop up every time i used the menu, not including the first run. */
void menu(void)
{
char tool = 1;
int r = 0;
while (r != 1)
{
printf("\n ---Select a tool.---\n\n ------------------\n\n C = Calculator\n\n D = Date/Time\n\n ");
tool = getc(stdin);
clrnl(); /* This seems to be the only line where clrnl(); has a positive effect. */
switch (tool)
{
case 'D':
printf("\n You selected Date/Time ");
D_Time();
r = 1;
break;
case 'C':
printf("\n You selected Calculator ");
cal();
r = 1;
break;
case 'd':
printf("\n You selected Date/Time ");
D_Time();
r = 1;
break;
case 'c':
printf("\n You selected Calculator ");
cal();
r = 1;
break;
case '\n':
printf("\n Error!! -- Newline character detected!! Try again.\n\n");
r = 0;
break;
default:
printf("\n Error!! -- Unknown. Try again.\n\n");
r = 0;
break;
}
}
}
int main()
{
char lx;
do
{
menu();
printf("\n Enter 'x' to exit.\n\n ");
scanf(" %c", &lx);
if (lx != 'x')
{
continue;
}
}
while (lx != 'x');
return 0;
}
Usually a context menu is not waiting for the user to press enter before getting the option while your loop does it.
In order to select the option when pressing the relevant key instead waiting for the "enter" you can call OS specific functions. Under linux reconfigure your terminal using system ("/bin/stty raw") just before entering in your while loop and than leave things as they are. Under windows consider _getch() instead of getc(stdin).
Remember that getchar() and getc() return an integer and not a char.
Doing this you have a more reactive menu and you don't need to handle the extra chars generated.
The problem is when you input your selection, for example D, and you press enter key, various things can happen. If you're on windows, your application will recieve characters: 'D', '\r', '\n'. On linux, it will be 'D','\n'. To eat up any extra input read by scanf use this simple trick:
scanf(" %c*", &lx);
The * in format string will skip your carriage return and newline characters.
This is changed code for tool selection:
printf("\n ---Select a tool.---\n\n ------------------\n\n C = Calculator\n\n D = Date/Time\n\n ");
scanf(" %c*", &tool);
This is the part for the exit input:
printf("\n Enter 'x' to exit.\n\n ");
scanf(" %c*", &lx);
You can further put more switch cases together like this to reduce code duplication:
case 'D':
case 'd':
printf("\n You selected Date/Time ");
D_Time();
r = 1;
break;
This will work, because there's no break statement after case 'D', so if this case happens, the code continues pass the case 'd': line into the corresponding code.