I have a little problem with one of my assignments. It's about a problem that goes as follows:
Using the Switch statement, write a program that reads values until the ENTER key is pressed. If the characters "Newline", "Tab", or "Backspace" are pressed, output their names.
Here's what I did so far.
int main(){
char x;
x=getchar();
while(x!='\r')
{
switch (x)
{
case '\t':
printf("Tab!");
break;
case '\n':
printf("Newline!");
break;
case '\b':
printf("Backspace!");
break;
default: break;
}
x = getchar();
}
/* switch(x){
case '\r': break;
case '\t': printf("Tab!");
case '\n': printf("Newline!");
case '\b': printf("Backspace!");
default: x=getch();
} */
return 0;
}
My problem is that every time I read a character using getch() or scanf() I press enter, so... the program doesn't do much. Do you have any ideas on how I could resolve this problem? Also isn't Newline same key as Enter?
I am using MinGW64 in Windows 10 and VScode as the IDE.
It's not clear from the requirement that the program should response after taking one character. May be no additional work is needed, as the output is the same, regardless of the line buffering of the input.
On Linux, you can move the terminal into 'raw' mode (either using system calls, or using stty). No such utility for Windows. If you MUST, look at: Get key press in windows console
Given this is basic Unix excercize, I doubt you need to implement the above.
Related
I need switch case in while. Every case in the switch is part of the menu which does something. Also I need to handle all the inputs from switch, so default isn't good choice for me. I need to leave while completely when user press CTRL + Z in CMD, where program is happening in order to free memory and quit program. This code is only simplified version of mine. Could you please help me? It's the last thing I need to do to have completed task. Thank you.
while (1) {
scanf("%c", &choose);
switch (choose) {
case 1:
//something
break;
case 2:
// something
break;
// no default!
}
}
If the user presses Ctrl+Z on Windows, your program will see that as an end of input file (stdin). When scanf tries to read a byte from stdin, it will fail and return -1 (EOF), while the usual return value is 1 (i.e. number of items read).
So you can use the following code:
while (scanf("%c", &choose) == 1)
{
switch (choose) {
case 1:
//something
break;
case 2:
// something
break;
// no default!
}
}
I put the return value of scanf into the while's condition. When scanf cannot read a byte from stdin anymore, the loop will terminate.
Your problem in handling control-Z is not leaving the switch statement, it is leaving the while statement.
On a Windows system, when the user presses control-Z at the start of an input line, the software will act similarly to an end-of-file. Notably, when scanf("%c", &choose); executes, there will be no input for it to read, so nothing will be stored in choose. Instead, scanf will return EOF to indicate no match to "%c"could be made (because no input was available). However, your code does not test the return value of scanf, so it will not see this. One way to fix this is:
while (1)
{
int n = scanf("%c", choose);
if (n != 1) // Did scanf match %c and store something in choose?
break; // No, so break out of the while loop.
switch (choose)
…
}
Then there is the matter of what should be in the switch statement. You say you “need to handle all the inputs from switch.” A char typically has 256 possible values. So, unless you have 256 case labels in the switch, you are not handling all the inputs in the switch statement. You should have a default label.
If any of the cases inside the switch needs to cause execution to leave the while statement, you can arrange this in various ways. One way is to use a variable to indicate that:
while (1)
{
int n = scanf("%c", choose);
if (n != 1) // Did scanf match %c and store something in choose?
break; // No, so break out of the while loop.
int LeaveWhile = 0;
switch (choose)
{
case 'A':
// Code to handle A input.
break;
case 'B':
…
case something: // Some case where we want to end the loop.
LeaveWhile = 1; // Indicate end of loop.
break;
…
default:
// Code to handle remaining values.
break;
}
// If it is time to leave the while loop, break out of it.
if (LeaveWhile)
break;
}
Before describing how I would handle the problem of the "break" keyword being overused in C, there is another issue you may hit that should be mentioned.
The behavior of control-C and control-Z vary with the OS and the TTY (console) settings.
Linux, with default TTY settings, will cause an interrupt with control-C, which if not handled will end the program. control-Z will suspend the program. By default, in neither case is the character delivered to the program, and so it can not be read by scanf, and it will not be dispatched by the switch statement.
Windows will also interrupt with control-C by default.
I mention this because you depend on reading control characters in your input parsing. If you aren't having the behavior you want, consider looking at TTY settings.
As for exiting the "while(1)" from inside the switch, the standard way is to change the loop from "while(1){}" to "int looping =1; while (looping){}", and set looping to 0 inside the body of the while loop.
In some cases, I will do this with (cover the eyes of the children watching) a goto to a label after the while body. I prominently display goto label, and reverse indent it to outside the level of the while loop body.
Sometimes the break condition is more natural, and sometimes the goto is clearer. If you don't have an aesthetic preference for one over the other, you might default to the break condition rather than the goto.
Add a flag at the top of the loop that gets set when you want to break out, then do so after the switch:
while (1) {
int breakout = 0;
scanf("%c", &choose);
switch (choose) {
case 1:
//something
break;
case 2:
// something else
// exit the loop in this case
breakout = 1;
break;
}
if (breakout) break;
}
you have also *do - while* loop.
bool continueLoop = true;
do
{
switch(something)
{
case smth1:
/* ... */
break
case smth2:
/* ... */
continueLoop = false; //whis will exit the loop
break;
default
/* ... */
break;
}
}while(continueLoop)
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 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.
K&R C 1-10 reads:
"Write a program to copy its input to its output, replacing each tab by \t, each backspace by \b, and each backslash by \. This makes tabs and backspace visible in an unambiguous way."
I have the following code and it does not work with the backspace character because the terminal eats the character. It doesn't seem like there's a solution with the material covered in the book so far. What would the solution be?
#include <stdio.h>
main()
{
int c;
while((c = getchar()) != EOF) {
switch (c) {
case '\t':
printf("\\t");
break;
case '\b':
printf("\\b");
break;
case '\\':
printf("\\\\");
break;
default:
putchar(c);
}
}
}
This is because the Operating System is handling terminal IO, and processes the characters from the keyboard before your program gets to see them.
If you are on a Unix/Linux system, you could wrap the execution of your program like this:
$ stty -icanon -echo; ./a.out; stty icanon echo
This will disable the terminal driver from processing the input in some specific ways: icanon enables the handling of of things like backspace processing, while echo causes the characters you type to be printed. Since your program echos the characters itself, you can turn echo off. The only problem with this is that -icanon also stops EOF processing, so you will need to add an extra condition to get out of the loop:
#include <stdio.h>
#define CTRL(x) (x & 0x1f)
main()
{
int c;
while((c = getchar()) != EOF && c != CTRL('d')) {
...
It is also a good idea while testing programs like this to run them in a separate window, so you can kill the entire session quickly and easily if you end up in a strange terminal mode!
Nothing wrong here. If you were to run this program on a file that contained the backspace character, it would properly convert it. For terminal input, the program will not receive the backspace as it is managed by the input routines.
When searching for everything about getchar() function in this really great site, I found this post: Why doesn't getchar() wait for me to press enter after scanf()?
#include <stdio.h>
int main()
{
int value;
printf("1. option 1.\n2. option 2.\n3. option 3.\n4. Exit\n\nMake an option: ");
scanf("%d", &value);
switch (value)
{
case 1:
printf("you selected the option 1.");
break;
case 2:
printf("you selected the option 2.");
break;
case 3:
printf("you selected the option 3.");
break;
case 4:
printf("goodbye");
break;
default:
printf("thats not an option");
break;
}
getchar();//here is the question,why it's useful ?
return 0;
}
I understand the whole program, and I understand that each time it is called, getchar reads the next input character from a text stream and returns that as its value. That is, after
c = getchar();
the variable c contains the next character of input. The characters normally come from the
keyboard.
But here is the question: why did the programmer call getchar() at the end of the program?
this practice is used especially for console applications, that way you force the program to not stop until you press a key so you can read the output. Usually the console closes when the program execution ends
The last getchar() function is there in order for the application to wait for a keypress before exit.
Generally in console apps, when you work in an IDE and run the code, a terminal window pops up runs the code, and as soon as the main function ends the terminal window also vanishes, and the users cannot see the last output. We need to make some way that the screen waits. One way is to make the program from being terminated by waiting for an input. Some people uses unbuffered input function at the end, so whenever you press a key the function reads it and returns immediately. In this case you use getchar which is a buffered input function, in this case, when you have finished inspecting the screen, press any character and press enter which will return from the function and after that terminate the program. You could have used scanf or other ways to hold the screen too.
It is good for debugging, please remove it from final release.