如何避免使用 getchar() 按下 Enter 来仅读取单个字符?

发布于 2024-08-12 14:57:54 字数 548 浏览 3 评论 0原文

在下一个代码中:

#include <stdio.h>

int main(void) {   
  int c;   
  while ((c=getchar())!= EOF)      
    putchar(c); 
  return 0;
}

我必须按 Enter 打印我用 getchar 输入的所有字母,但我不想这样做,我想做的是按该字母并立即看到我重复介绍的字母,而无需按 Enter。例如,如果我按字母“a”,我想在它旁边看到另一个“a”,依此类推:

aabbccddeeff.....

但是当我按“a”时什么也没有发生,我可以写其他字母,并且仅当我按“a”时才会出现副本按Enter

abcdef
abcdef

我该怎么做?

我在Ubuntu下使用命令cc -o example example.c进行编译。

In the next code:

#include <stdio.h>

int main(void) {   
  int c;   
  while ((c=getchar())!= EOF)      
    putchar(c); 
  return 0;
}

I have to press Enter to print all the letters I entered with getchar, but I don't want to do this, what I want to do is to press the letter and immediately see the the letter I introduced repeated without pressing Enter. For example, if I press the letter 'a' I want to see an other 'a' next to it, and so on:

aabbccddeeff.....

But when I press 'a' nothing happens, I can write other letters and the copy appears only when I press Enter:

abcdef
abcdef

How can I do this?

I am using the command cc -o example example.c under Ubuntu for compiling.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

离不开的别离 2024-08-19 14:57:54

这取决于您的操作系统,如果您处于类似 UNIX 的环境中,默认情况下会启用 ICANON 标志,因此输入会被缓冲,直到下一个 '\n'EOF。通过禁用规范模式,您将立即获得字符。这在其他平台上也是可能的,但没有直接的跨平台解决方案。

编辑:我看到你指定你使用 Ubuntu。我昨天刚刚发布了类似的内容,但请注意,这将禁用终端的许多默认行为。

#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;
}

您会注意到,每个字符出现两次。这是因为输入立即回显到终端,然后您的程序也使用 putchar() 将其放回。如果要取消输入与输出的关联,还必须关闭 ECHO 标志。您只需将相应的行更改为:

newt.c_lflag &= ~(ICANON | ECHO); 

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); 
酷到爆炸 2024-08-19 14:57:54

在 Linux 系统上,您可以使用 stty 命令修改终端行为。默认情况下,终端将缓冲所有信息,直到按下 Enter,然后再将其发送到 C 程序。

一个快速、肮脏且不是特别便携的示例,用于从程序本身内部更改行为:

#include<stdio.h>
#include<stdlib.h>

int main(void){
  int c;
  /* use system call to make terminal send all keystrokes directly to stdin */
  system ("/bin/stty raw");
  while((c=getchar())!= '.') {
    /* type a period to break out of the loop, since CTRL-D won't work raw */
    putchar(c);
  }
  /* use system call to set terminal behaviour to more normal behaviour */
  system ("/bin/stty cooked");
  return 0;
}

请注意,这并不是真正的最佳选择,因为它只是假设 stty Cooked 是程序退出时您想要的行为,而不是检查原始终端设置是什么。此外,由于在原始模式下会跳过所有特殊处理,因此许多按键序列(例如 CTRL-CCTRL-D)实际上不会按您期望的方式工作无需在程序中显式处理它们。

您可以 man stty 对终端行为进行更多控制,具体取决于您想要实现的目标。

On a linux system, you can modify terminal behaviour using the stty command. By default, the terminal will buffer all information until Enter is pressed, before even sending it to the C program.

A quick, dirty, and not-particularly-portable example to change the behaviour from within the program itself:

#include<stdio.h>
#include<stdlib.h>

int main(void){
  int c;
  /* use system call to make terminal send all keystrokes directly to stdin */
  system ("/bin/stty raw");
  while((c=getchar())!= '.') {
    /* type a period to break out of the loop, since CTRL-D won't work raw */
    putchar(c);
  }
  /* use system call to set terminal behaviour to more normal behaviour */
  system ("/bin/stty cooked");
  return 0;
}

Please note that this isn't really optimal, since it just sort of assumes that stty cooked is the behaviour you want when the program exits, rather than checking what the original terminal settings were. Also, since all special processing is skipped in raw mode, many key sequences (such as CTRL-C or CTRL-D) won't actually work as you expect them to without explicitly processing them in the program.

You can man stty for more control over the terminal behaviour, depending exactly on what you want to achieve.

岛歌少女 2024-08-19 14:57:54

getchar() 是一个标准函数,在许多平台上要求您按 ENTER 才能获取输入,因为平台会缓冲输入,直到按下该键为止。许多编译器/平台支持不关心 ENTER 的非标准 getch() (绕过平台缓冲,将 ENTER 视为另一个键)。

getchar() is a standard function that on many platforms requires you to press ENTER to get the input, because the platform buffers input until that key is pressed. Many compilers/platforms support the non-standard getch() that does not care about ENTER (bypasses platform buffering, treats ENTER like just another key).

乖乖哒 2024-08-19 14:57:54

I/O 是操作系统的一项功能。在许多情况下,操作系统不会将键入的字符传递给程序,直到按下 ENTER 键。这允许用户在将输入发送到程序之前修改输入(例如退格和重新键入)。对于大多数用途来说,这种方法效果很好,为用户提供了一致的界面,并使程序不必处理这个问题。在某些情况下,程序需要在按键时从按键中获取字符。

C 库本身处理文件,并不关心数据如何进入输入文件。因此,语言本身无法在按下按键时获取按键;相反,这是特定于平台的。由于您没有指定操作系统或编译器,我们无法为您查找。

此外,为了提高效率,标准输出通常会被缓冲。这是由 C 库完成的,因此有一个 C 解决方案,即在写入每个字符后执行 fflush(stdout); 。之后,是否立即显示字符取决于操作系统,但我熟悉的所有操作系统都会立即显示输出,所以这通常不是问题。

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.

时光无声 2024-08-19 14:57:54

我喜欢卢卡斯的回答,但我想详细说明一下。 termios.h 中有一个名为 cfmakeraw() 的内置函数,man 描述为:

cfmakeraw() sets the terminal to something like the "raw" mode of the
old Version 7 terminal driver: input is available character by
character, echoing is disabled, and all special processing of
terminal input and output characters is disabled. [...]

这基本上与 Lucas 建议的相同更多,您可以在手册页中看到它设置的确切标志: termios (3)

使用案例

int c = 0;
static struct termios oldTermios, newTermios;

tcgetattr(STDIN_FILENO, &oldTermios);
newTermios = oldTermios;

cfmakeraw(&newTermios);

tcsetattr(STDIN_FILENO, TCSANOW, &newTermios);
c = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldTermios);

switch (c) {
    case 113: // q
        printf("\n\n");
        exit(0);
        break;
    case 105: // i
        printf("insert\n");
        break;
    default:
        break;

I like Lucas answer, but I would like to elaborate it a bit. There is a built-in function in termios.h named cfmakeraw() which man describes as:

cfmakeraw() sets the terminal to something like the "raw" mode of the
old Version 7 terminal driver: input is available character by
character, echoing is disabled, and all special processing of
terminal input and output characters is disabled. [...]

This basically does the same as what Lucas suggested and more, you can see the exact flags it sets in the man pages: termios(3).

Use case

int c = 0;
static struct termios oldTermios, newTermios;

tcgetattr(STDIN_FILENO, &oldTermios);
newTermios = oldTermios;

cfmakeraw(&newTermios);

tcsetattr(STDIN_FILENO, TCSANOW, &newTermios);
c = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldTermios);

switch (c) {
    case 113: // q
        printf("\n\n");
        exit(0);
        break;
    case 105: // i
        printf("insert\n");
        break;
    default:
        break;
红颜悴 2024-08-19 14:57:54

由于您正在开发 Unix 衍生版本(Ubuntu),因此有一种方法可以做到这一点 - 不推荐,但它会起作用(只要您可以准确地键入命令):

echo "stty -g $(stty -g)" > restore-sanity
stty cbreak
./your_program

当您对程序感到厌倦时,使用中断来停止程序。

sh restore-sanity
  • “echo”行将当前终端设置保存为 shell 脚本,以便恢复它们。
  • 'stty' 行关闭大部分特殊处理(例如,Control-D 无效),并在字符可用时立即将其发送到程序。这意味着您无法再编辑您的打字。
  • “sh”行恢复您原来的终端设置。

如果“stty sane”能够足够准确地恢复您的设置以达到您的目的,您就可以节省开支。 “-g”的格式不可跨版本“stty”移植(因此在 Solaris 10 上生成的内容在 Linux 上不起作用,反之亦然),但该概念在任何地方都适用。 'stty sane' 选项并不是普遍可用的,AFAIK(但在 Linux 上)。

Since you are working on a Unix derivative (Ubuntu), here is one way to do it - not recommended, but it will work (as long as you can type commands accurately):

echo "stty -g $(stty -g)" > restore-sanity
stty cbreak
./your_program

Use interrupt to stop the program when you are bored with it.

sh restore-sanity
  • The 'echo' line saves the current terminal settings as a shell script that will restore them.
  • The 'stty' line turns off most of the special processing (so Control-D has no effect, for example) and sends characters to the program as soon as they are available. It means you cannot edit your typing any more.
  • The 'sh' line reinstates your original terminal settings.

You can economize if 'stty sane' restores your settings sufficiently accurately for your purposes. The format of '-g' is not portable across versions of 'stty' (so what is generated on Solaris 10 won't work on Linux, or vice versa), but the concept works everywhere. The 'stty sane' option is not universally available, AFAIK (but is on Linux).

零崎曲识 2024-08-19 14:57:54

您可以包含“ncurses”库,并使用 getch() 而不是 getchar()

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

死开点丶别碍眼 2024-08-19 14:57:54

如何避免使用 getchar()Enter

首先,终端输入通常是行缓冲或完全缓冲的。这意味着操作系统将来自终端的实际输入存储在缓冲区中。通常,当例如在 stdin 中发出信号/提供 \n 时,此缓冲区会刷新到程序。这是通过按 Enter 来完成的。

getchar() 位于链的末尾。它没有能力实际影响缓冲过程。


我该怎么做?

首先放弃 getchar(),如果您不想使用特定的系统调用来明确更改终端的行为,如其他答案中所解释的那样。

不幸的是,没有标准库函数,并且没有可移植的方法来在单个字符输入时刷新缓冲区。但是,存在基于实现的解决方案和不可移植的解决方案。


在 Windows/MS-DOS 中,有 getch()conio.h 头文件,它完全可以完成您想要的事情 - 读取单个字符,而无需等待换行符刷新缓冲区。

getch()getche()getch() 不会立即在控制台输出实际输入的字符,而 getche() 确实如此。附加的“e”代表echo

示例:

#include <stdio.h>
#include <conio.h>   

int main (void) 
{
    int c;

    while ((c = getche()) != EOF)
    {
        if (c == '\n')
        {
            break;
        }

        printf("\n");
    }

    return 0;
}

在 Linux 中,获得直接字符处理和输出的一种方法是使用 cbreak ()echo()< /a> 选项和 getch() 和 < ncurses-library 中的 href="https://linux.die.net/man/3/refresh" rel="nofollow noreferrer">refresh() 例程。

请注意,您需要使用 < 初始化所谓的标准屏幕 code>initscr() 并使用 关闭相同的内容endwin() 例程。

示例:

#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;
}

注意:您需要使用 -lncurses 选项调用编译器,以便链接器可以搜索并找到 ncurses-library。

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

First of all, terminal input is commonly either line or fully buffered. This means that the operating system stores the actual input from the terminal in a buffer. Usually, this buffer is flushed to the program when e.g. \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.

格子衫的從容 2024-08-19 14:57:54

是的,你也可以在 Windows 上执行此操作,下面是使用 conio.h 库的代码

#include <iostream> //basic input/output
#include <conio.h>  //provides non standard getch() function
using namespace std;

int main()
{  
  cout << "Password: ";  
  string pass;
  while(true)
  {
             char ch = getch();    

             if(ch=='\r'){  //when a carriage return is found [enter] key
             cout << endl << "Your password is: " << pass <<endl; 
             break;
             }

             pass+=ch;             
             cout << "*";
             }
  getch();
  return 0;
}

yes you can do this on windows too, here's the code below, using the conio.h library

#include <iostream> //basic input/output
#include <conio.h>  //provides non standard getch() function
using namespace std;

int main()
{  
  cout << "Password: ";  
  string pass;
  while(true)
  {
             char ch = getch();    

             if(ch=='\r'){  //when a carriage return is found [enter] key
             cout << endl << "Your password is: " << pass <<endl; 
             break;
             }

             pass+=ch;             
             cout << "*";
             }
  getch();
  return 0;
}
风为裳 2024-08-19 14:57:54

我在当前正在做的作业中遇到了这个问题。
它还取决于您从哪个输入中获取。
我用于

/dev/tty

在程序运行时获取输入,因此需要是与命令关联的文件流。

在 ubuntu 机器上,我必须测试/目标,它需要的不仅仅是

system( "stty -raw" );

或者

system( "stty -icanon" );

我必须添加 --file 标志,以及命令的路径,如下所示:

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

现在一切都正常了。

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.

浪荡不羁 2024-08-19 14:57:54

这段代码对我有用。注意:这不是标准库的一部分,即使大多数编译器(我使用 GCC)都支持它。

#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 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.

心如狂蝶 2024-08-19 14:57:54

可以创建一个新函数来检查 Enter

#include <stdio.h>

char getChar()
{
    printf("Please enter a char:\n");

    char c = getchar();
    if (c == '\n')
    {
        c = getchar();
    }

    return c;
}

int main(int argc, char *argv[])
{
    char ch;
    while ((ch = getChar()) != '.')
    {
        printf("Your char: %c\n", ch);
    }

    return 0;
}

Can create a new function that checks for Enter:

#include <stdio.h>

char getChar()
{
    printf("Please enter a char:\n");

    char c = getchar();
    if (c == '\n')
    {
        c = getchar();
    }

    return c;
}

int main(int argc, char *argv[])
{
    char ch;
    while ((ch = getChar()) != '.')
    {
        printf("Your char: %c\n", ch);
    }

    return 0;
}
献世佛 2024-08-19 14:57:54

您可以使用 _getch() 而不是中的 getch()

you can use _getch() instead of getch() from <conio.h>

耳钉梦 2024-08-19 14:57:54

默认情况下,C 库会缓冲输出,直到看到返回为止。要立即打印结果,请使用 fflush

while((c=getchar())!= EOF)      
{
    putchar(c);
    fflush(stdout);
}

By default, the C library buffers the output until it sees a return. To print out the results immediately, use fflush:

while((c=getchar())!= EOF)      
{
    putchar(c);
    fflush(stdout);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文