My C code:
int c;
c = getchar();
while (c != EOF) {
putchar(c);
c = getchar();
}
Why does this program react like this on inputting hello?
hello
hello
and not like:
hheelloo
Your input is hello and not h e l l o right?
So the input you give is buffered until you press enter.
When you type, a console grabs the output from the keyboard, echoing it back to you.
getchar() operates on an input stream, which is typically configured with "Canonical input" turned on. Such a configuration reduces the CPU time spend polling the input for a buffering scheme where the input is buffered, until certain events occur which signal the buffer to expand. Pressing the enter key (and hitting control D) both tend to flush that buffer.
#include <unistd.h>
int main(void){
int c;
static struct termios oldt;
static struct termios newt;
/* Fetch the old io attributes */
tcgetattr( STDIN_FILENO, &oldt);
/* copy the attributes (to permit restoration) */
newt = oldt;
/* Toggle canonical mode */
newt.c_lflag &= ~(ICANON);
/* apply the new attributes with canonical mode off */
tcsetattr( STDIN_FILENO, TCSANOW, &newt);
/* echo output */
while((c=getchar()) != EOF) {
putchar(c);
fflush(STDOUT_FILENO);
}
/* restore the old io attributes */
tcsetattr( STDIN_FILENO, TCSANOW, &oldt);
return 0;
}
Your terminal probably only writes your input to stdin when you press enter. Try typing something, backspace and write something else; if you don't see the originally typed characters, it means that your terminal was waiting for you to compose the line before sending the data to the program.
If you want raw terminal access (e.g. react to key-down and key-up), you should try some terminal library like ncurses.
Because the default for stdin when it refers to the keyboard is to be line buffered.
That means you only get to see full lines, and not single characters.
Imagine you ask a friend of yours what his phone number is ... but he must write it down on a piece of paper. You don't get the number digit-by-digit as he writes them: you get all of the number when he gives you the piece of paper :)
The standard input/output streams can be buffered which means your input may not be echo'd to the screen until a whitespace character (for example) is encountered.
getchar reads the input from input stream which is available only after ENTER key is pressed. till then you see only the echoed result from the console To achieve the result you want you could use something like this
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
int getCHAR( ) {
struct termios oldt,
newt;
int ch;
tcgetattr( STDIN_FILENO, &oldt );
newt = oldt;
newt.c_lflag &= ~( ICANON | ECHO );
tcsetattr( STDIN_FILENO, TCSANOW, &newt );
ch = getchar();
putchar(ch);
tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
return ch;
}
void main() {
int c;
c = getCHAR();
while (c != 'b') {
putchar(c);
c = getCHAR();
}
}
Related
This question already has answers here:
How to avoid pressing Enter with getchar() for reading a single character only?
(14 answers)
Closed 5 years ago.
I am making 2048 game in C and I need help. Moves are made by pressing W,A,S,D keys e.g. W is for moving up, S for down.
However, after every letter you have to press enter to accept it. How can I make it work without pressing enter?
There is no standard library function in c to accomplish this; instead you will have to use termios functions to gain control over the terminal and then reset it back after reading the input.
I came across some code to read input from stdin without waiting for a delimiter here.
If you are on linux and using a standard c compiler then getch() will not be easily available for you. Hence i have implemented the code in the link and you just need to paste this code and use the getch() function normally.
#include <termios.h>
#include <stdio.h>
static struct termios old, new;
/* Initialize new terminal i/o settings */
void initTermios(int echo)
{
tcgetattr(0, &old); /* grab old terminal i/o settings */
new = old; /* make new settings same as old settings */
new.c_lflag &= ~ICANON; /* disable buffered i/o */
new.c_lflag &= echo ? ECHO : ~ECHO; /* set echo mode */
tcsetattr(0, TCSANOW, &new); /* use these new terminal i/o settings now */
}
/* Restore old terminal i/o settings */
void resetTermios(void)
{
tcsetattr(0, TCSANOW, &old);
}
/* Read 1 character - echo defines echo mode */
char getch_(int echo)
{
char ch;
initTermios(echo);
ch = getchar();
resetTermios();
return ch;
}
/* Read 1 character without echo */
char getch(void)
{
return getch_(0);
}
int main()
{
int ch;
ch = getch();//just use this wherever you want to take the input
printf("%d", ch);
return 0;
}
what you are asking for is function named kbhit() which returns true if user presses a key on the keyboard. you can use this function to get input from the use too like see
char c= ' ';
while(1){
if(kbhit())
c=getch();
if(c=='q')// condition to stop the infinite loop
break;
}
i was trying to do the execize 1.10 of k&r so this is :
/*
* Exercise 1.10
*
* 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 tab
* and backspaces visible in an unambiguous way.
*
*/
#include <stdio.h>
int main()
{
int c;
while ((c = getchar()) != EOF) {
if (c == '\t')
printf("\\t");
else if (c == '\b')
printf("\\b");
else if (c == '\\')
printf("\\\\");
else
printf("%c", c);
}
}
If i compile this code with gcc -std=c99 1.10.c -o test
it doesn't print \b if i use Backspace. Why? And how I could try to get \b pressing Backspace in Linux ?
A guy has said me:
Your program probably doesn't see that backspace. Terminals buffer by line, by default. So does stdin. Well, stdin's buffering is IDB.
Usually, the console interprets special characters, like backspace or Ctrl-d. You can instruct the console to not do this with stty.
Before you run the program, you can set the tty to ignore backspace mode with
stty erase ''
and restore the console afterwards with
stty sane
This passes the backspace characters Ctrl-h unchanged to the program you're running.
If this doesn't show any difference, then the backspace key may be mapped to DEL instead of Ctrl-h.
In this case you can just start your program and type Ctrl-h everywhere you would have used the backspace key. If you want to catch the backspace key nevertheless, you must also check for DEL, which is ASCII 127
/* ... */
else if (c == '\b' || c == 127)
printf("\\b");
#include <termios.h>
#include <unistd.h>
int mygetch(void)
{
struct termios oldt,
newt;
int ch;
tcgetattr( STDIN_FILENO, &oldt );
newt = oldt;
newt.c_lflag &= ~( ICANON | ECHO );
tcsetattr( STDIN_FILENO, TCSANOW, &newt );
ch = getchar();
tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
return ch;
}
use getch then get the ASCII for backsapce using it, no need for enter or to clean input buffer and it returns an int.
Enjoy
Your code doesn't work because it's intended to run on very old UNIX machines. On modern terminals, the backspace character won't be considered.
I have a infinite loop like the following one, and within this loop, I want to continuously check the keyboard to see if the escape key (ESC) has been pressed or not. If it is pressed, then the loop should be broken. How I can do this in C? (I am using gcc, and do access to pthreads as well in case this must be done via threads)
while(1){
//do something
//check for the ESC key
}
This is heavily system dependent. In Unix/Linux systems, the default terminal handler gathers lines and only notifies the program when a full line is available (after Enter is hit.) If you instead want keystrokes immediately, you need to put the terminal into non-canonical mode:
#include <termios.h>
struct termios info;
tcgetattr(0, &info); /* get current terminal attirbutes; 0 is the file descriptor for stdin */
info.c_lflag &= ~ICANON; /* disable canonical mode */
info.c_cc[VMIN] = 1; /* wait until at least one keystroke available */
info.c_cc[VTIME] = 0; /* no timeout */
tcsetattr(0, TCSANOW, &info); /* set immediately */
Once you've done that, you can use any calls that read from stdin and they will return keys without waiting for the end of the line. You can in addition set c_cc[VMIN] = 0 to cause it to not wait for keystrokes at all when you read from stdin.
If, however, you're reading stdin with stdio FILE related calls (getchar, etc), setting VMIN = 0 will make it think you've reached EOF whenever there are no keys available, so you'll have to call clearerr after that happens to try to read more characters. You can use a loop like:
int ch;
while((ch = getchar()) != 27 /* ascii ESC */) {
if (ch < 0) {
if (ferror(stdin)) { /* there was an error... */ }
clearerr(stdin);
/* do other stuff */
} else {
/* some key OTHER than ESC was hit, do something about it? */
}
}
After you're done, you probably want to be sure to set the terminal back into canonical mode, lest other programs (such as your shell) get confused:
tcgetattr(0, &info);
info.c_lflag |= ICANON;
tcsetattr(0, TCSANOW, &info);
There are also other things you can do with tcsetattr -- see then manual page for details. One thing that might suffice for your purposes is setting an alternative EOL character.
If the main job you're doing can be placed within this main loop, you could go for using STDIN in non-blocking mode. You still have a problem with the terminal which does line-buffering normally. You shall put the terminal to raw mode as well.
What about using Ctrl-C (interrupt)?
Non-blocking means that the read() system call always returns immediately even if there are no new bytes in the file. On Linux/Unix you can make STDIN nonblocking this way:
#include <unistd.h>
#include <fcntl.h>
fcntl(0, F_SETFL, O_NONBLOCK); /* 0 is the stdin file decriptor */
This is what you want:
#include <stdio.h>
#include <conio.h>
void main() {
int c;
while((c = getch()) != EOF )
if(c == 27) break;
/* 27 is the ASCII code for Esc */
}
I'm trying to make a simple command that pauses for user input. I think it'll be useful in Bash scripts.
Here's my code:
#include <stdio.h>
int main() {
char key[1];
puts("Press any key to continue...");
fgets(key,1,stdin);
}
It doesn't even pause for user input.
I tried earlier to use getch() (ncurses). What happened is, the screen went blank and when I pressed a key, it went back to what was originally on the screen, and I saw:
$ ./pause
Press any key to continue...
$
It's somewhat what I wanted. But all I want is the equivalent of the pause command in DOS/Windows (I use Linux).
From the GNU C Library Manual:
Function: char * fgets (char *s, int count, FILE *stream)
The fgets
function reads characters from the stream stream up to and including a
newline character and stores them in the string s, adding a null
character to mark the end of the string. You must supply count
characters worth of space in s, but the number of characters read is
at most count − 1. The extra character space is used to hold the null
character at the end of the string.
So, fgets(key,1,stdin); reads 0 characters and returns. (read: immediately)
Use getchar or getline instead.
Edit: fgets also doesn't return once count characters are available on the stream, it keeps waiting for a newline and then reads count characters, so "any key" might not be the correct wording in this case then.
You can use this example to avoid line-buffering:
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
int mygetch ( void )
{
int ch;
struct termios oldt, newt;
tcgetattr ( STDIN_FILENO, &oldt );
newt = oldt;
newt.c_lflag &= ~( ICANON | ECHO );
tcsetattr ( STDIN_FILENO, TCSANOW, &newt );
ch = getchar();
tcsetattr ( STDIN_FILENO, TCSANOW, &oldt );
return ch;
}
int main()
{
printf("Press any key to continue.\n");
mygetch();
printf("Bye.\n");
}
I'm not sure how to add a comment under a post so this isn't really an answer but a comment,
In linux Stdin is buffered so you need to flush it which is what pressing 'enter' does on your terminal. It seems you want to read from an unbuffered stream i.e you want to react to a keypress imediately (without the need to explicitly flush it).
You can create your own unbuffered stream using a file discriptor and then read from it using "getc", you may have to use termios to setup unbuffered input as others have suggested.
This is a simple method that worked for me on Windows 10:
#include <stdlib.h>
void pause(void);
int main(void)
{
printf("Testing pause.");
}
void pause(void)
{
system("pause");
}
I am using termios as suggested in a previous question I asked but now am asking if there is a way get backspace to work whilst using termios in non-canonical mode. I am using termios to have not have an echo If I use &=ECHO and &=ICANON this is the result I want, the keyboard input is sent to putchar() as soon as the key is press and displayed but the '\b' key is display as hex, if I do the opposite I can't see the text till enter is pressed but '\b' works.
I have looked up the manual and some other forums that and they said " not possible just don't make any mistakes", this would make sense seeing as how when I don't enter my password correctly in in a terminal on Ubuntu I can't backspace and change it. But I was making sure I haven't missed anything in the manual.
Code is to get input from stdin and not display empty lines.
#include <unistd.h>
#include <termios.h>
#include <errno.h>
#include <stdio.h>
#define ECHOFLAGS (ECHO)
int setecho(int fd, int onflag);
int first_line(int *ptrc);
int main(void){
struct termios old;
tcgetattr(STDIN_FILENO,&old);
setecho(STDIN_FILENO,0);
int c;
while((c = getchar())!= 4) //no end of file in non-canionical match to control D
first_line(&c);
tcsetattr(STDIN_FILENO,&old);
return 0;
}
int setecho(int fd, int onflag){
int error;
struct termios term;
if(tcgetattr(fd, &term) == -1)
return -1;
if(onflag){ printf("onflag\n");
term.c_lflag &= ECHOFLAGS ; // I know the onflag is always set to 0 just
term.c_lflag &=ICANON; // testing at this point
}
else{ printf("else\n");
term.c_lflag &= ECHO;
term.c_lflag &=ICANON;
}
while (((error = tcsetattr(fd, TCSAFLUSH, &term)) ==-1 && (errno == EINTR)))
return error;
}
int first_line(int *ptrc){
if (*ptrc != '\n' && *ptrc != '\r'){
putchar(*ptrc);
while (*ptrc != '\n'){
*ptrc = getchar();
putchar(*ptrc);
}
}
else return 0;
return 0;
}
Thanks Lachlan
P.S on a side point in my research I noticed someone saying Termios isn't "Standard C" is this because it is system dependant? (only for comments)
How would you expect this to work? If the input characters are sent to your program immediately, then by the time the backspace character is recieved it's simply too late for the terminal to handle backspace - your program has already seen the previous character, so it can't be taken back.
For example, say the user presses A. Your program will receieve 'A' from getchar() and process it. Now the user presses backspace - now what should the terminal do?
So this implies that the only place you can handle backspace in non-canonical mode is in your program itself. When you receive the '\b' character from getchar(), you can handle it specially (just like you have special handling for '\n') - for example, remove the most recently entered character from a buffer.
It's implementation-dependent. On my machine, pressing backspace led to the byte 127 being read by read(). This code worked on my machine.
#include <unistd.h>
#include <stdio.h>
#include <termios.h>
#include <string.h>
#include <stdlib.h>
#define MAXBUFSIZE (10000U)
#define DEL (127)
int main(void) {
char buf[MAXBUFSIZE];
char c;
size_t top;
struct termios curterm;
tcgetattr(STDIN_FILENO, &curterm);
curterm.c_lflag &= ~(ICANON| ECHO);
curterm.c_cc[VTIME] = 0;
curterm.c_cc[VMIN] = 1;
tcsetattr(STDIN_FILENO, TCSANOW, &curterm);
top = 0;
while (read(STDIN_FILENO, &c, sizeof c) == 1) {
switch (c) {
case DEL:
if (top) {
--top;
const char delbuf[] = "\b \b";
write(STDOUT_FILENO, delbuf, strlen(delbuf));
}
break;
case '\n':
write(STDOUT_FILENO, &c, sizeof c);
write(STDOUT_FILENO, buf, top);
top = 0;
break;
default:
buf[top++] = c;
write(STDOUT_FILENO, &c, sizeof c);
break;
}
}
return 0;
}
When the user presses backspace the application receives some control code. The application has to interpret the control code and take a character out of its buffer (application buffer since you are not using the kernel buffer). If the application is doing any kind of echoing (eg: echoing stars in place of the password characters) then it will have to send some other control codes to move the cursor left and blank out the last star.
Both the codes received for delete or backspace and the codes you send to move the cursor depend on the type of terminal the user has, so before you can do any of this jiggery-pokery you have to detect the terminal type as well. Most programmers don't want to spend time reading the manual for hundreds of different types of terminal, so they generally use a library that hides all of this from them. One such library is curses.