How to avoid pressing Enter with getchar() for reading a single character only?

后端 未结 12 1619
长发绾君心
长发绾君心 2020-11-22 00:33

In the next code:

#include 

int main(void) {   
  int c;   
  while ((c=getchar())!= EOF)      
    putchar(c); 
  return 0;
}
相关标签:
12条回答
  • 2020-11-22 00:52

    I've had this problem/question come up in an assignment that I'm currently working on. It also depends on which input you are grabbing from. I am using

    /dev/tty
    

    to get input while the program is running, so that needs to be the filestream associated with the command.

    On the ubuntu machine I have to test/target, it required more than just

    system( "stty -raw" );
    

    or

    system( "stty -icanon" );
    

    I had to add the --file flag, as well as path to the command, like so:

    system( "/bin/stty --file=/dev/tty -icanon" );
    

    Everything is copacetic now.

    0 讨论(0)
  • 2020-11-22 00:54

    I/O is an operating system function. In many cases, the operating system won't pass typed character to a program until ENTER is pressed. This allows the user to modify the input (such as backspacing and retyping) before sending it to the program. For most purposes, this works well, presents a consistent interface to the user, and relieves the program from having to deal with this. In some cases, it's desirable for a program to get characters from keys as they are pressed.

    The C library itself deals with files, and doesn't concern itself with how data gets into the input file. Therefore, there's no way in the language itself to get keys as they are pressed; instead, this is platform-specific. Since you haven't specified OS or compiler, we can't look it up for you.

    Also, the standard output is normally buffered for efficiency. This is done by the C libraries, and so there is a C solution, which is to fflush(stdout); after each character written. After that, whether the characters are displayed immediately is up to the operating system, but all the OSes I'm familiar with will display the output immediately, so that's not normally a problem.

    0 讨论(0)
  • 2020-11-22 00:58

    This depends on your OS, if you are in a UNIX like environment the ICANON flag is enabled by default, so input is buffered until the next '\n' or EOF. By disabling the canonical mode you will get the characters immediately. This is also possible on other platforms, but there is no straight forward cross-platform solution.

    EDIT: I see you specified that you use Ubuntu. I just posted something similar yesterday, but be aware that this will disable many default behaviors of your terminal.

    #include<stdio.h>
    #include <termios.h>            //termios, TCSANOW, ECHO, ICANON
    #include <unistd.h>     //STDIN_FILENO
    
    
    int main(void){   
        int c;   
        static struct termios oldt, newt;
    
        /*tcgetattr gets the parameters of the current terminal
        STDIN_FILENO will tell tcgetattr that it should write the settings
        of stdin to oldt*/
        tcgetattr( STDIN_FILENO, &oldt);
        /*now the settings will be copied*/
        newt = oldt;
    
        /*ICANON normally takes care that one line at a time will be processed
        that means it will return if it sees a "\n" or an EOF or an EOL*/
        newt.c_lflag &= ~(ICANON);          
    
        /*Those new settings will be set to STDIN
        TCSANOW tells tcsetattr to change attributes immediately. */
        tcsetattr( STDIN_FILENO, TCSANOW, &newt);
    
        /*This is your part:
        I choose 'e' to end input. Notice that EOF is also turned off
        in the non-canonical mode*/
        while((c=getchar())!= 'e')      
            putchar(c);                 
    
        /*restore the old settings*/
        tcsetattr( STDIN_FILENO, TCSANOW, &oldt);
    
    
        return 0;
    }
    

    You will notice, that every character appears twice. This is because the input is immediately echoed back to the terminal and then your program puts it back with putchar() too. If you want to disassociate the input from the output, you also have to turn of the ECHO flag. You can do this by simply changing the appropriate line to:

    newt.c_lflag &= ~(ICANON | ECHO); 
    
    0 讨论(0)
  • 2020-11-22 00:59

    You could include the 'ncurses' library, and use getch() instead of getchar().

    0 讨论(0)
  • 2020-11-22 00:59

    "How to avoid pressing Enter with getchar()?"

    First of all, terminal input is commonly either line or fully buffered. This means that the operation system stores the actual input from the terminal into a buffer. Usually, this buffer is flushed to the program when f.e. \n was signalized/provided in stdin. This is f.e. made by a press to Enter.

    getchar() is just at the end of the chain. It has no ability to actually influence the buffering process.


    "How can I do this?"

    Ditch getchar() in the first place, if you don´t want to use specific system calls to change the behavior of the terminal explicitly like well explained in the other answers.

    There is unfortunately no standard library function and with that no portable way to flush the buffer at single character input. However, there are implementation-based and non-portable solutions.


    In Windows/MS-DOS, there are the getch() and getche() functions in the conio.h header file, which do exactly the thing you want - read a single character without the need to wait for the newline to flush the buffer.

    The main difference between getch() and getche() is that getch() does not immediately output the actual input character in the console, while getche() does. The additional "e" stands for echo.

    Example:

    #include <stdio.h>
    #include <conio.h>   
    
    int main (void) 
    {
        int c;
    
        while ((c = getche()) != EOF)
        {
            if (c == '\n')
            {
                break;
            }
    
            printf("\n");
        }
    
        return 0;
    }
    

    In Linux, a way to obtain direct character processing and output is to use the cbreak() and echo() options and the getch() and refresh() routines in the ncurses-library.

    Note, that you need to initialize the so called standard screen with the initscr() and close the same with the endwin() routines.

    Example:

    #include <stdio.h>
    #include <ncurses.h>   
    
    int main (void) 
    {
        int c;
    
        cbreak(); 
        echo();
    
        initscr();
    
        while ((c = getch()) != ERR)
        {
            if (c == '\n')
            {
                break;
            }
    
            printf("\n");
            refresh();
        }
    
        endwin();
    
        return 0;
    }
    

    Note: You need to invoke the compiler with the -lncurses option, so that the linker can search and find the ncurses-library.

    0 讨论(0)
  • 2020-11-22 01:02

    This code worked for me. Attention : this is not part of the standard library, even if most compilers (I use GCC) supports it.

    #include <stdio.h>
    #include <conio.h>
    int main(int argc, char const *argv[]) {
        char a = getch();
        printf("You typed a char with an ASCII value of %d, printable as '%c'\n", a, a);
        return 0;
    }
    

    This code detects the first key press.

    0 讨论(0)
提交回复
热议问题