如何避免使用getchar()按Enter键

jav*_*ier 70 c getchar

在下一个代码中:

#include <stdio.h>

int main(void) {   
  int c;   
  while ((c=getchar())!= EOF)      
    putchar(c); 
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

我必须按Enter下来打印我输入的所有字母getchar,但我不想这样做,我想要做的就是按下这封信,然后立即看到我重复介绍的信,而不是按下Enter.例如,如果我按下'a'字母,我想在旁边看到另一个'a',依此类推:

aabbccddeeff.....
Run Code Online (Sandbox Code Playgroud)

但当我按'a'时没有任何反应,我可以写其他字母,只有当我按下时才会出现副本Enter:

abcdef
abcdef
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?

我正在使用cc -o example example.cUbuntu下的命令进行编译.

Luc*_*cas 84

这取决于您的操作系统,如果您在类似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;
}
Run Code Online (Sandbox Code Playgroud)

你会注意到,每个角色出现两次.这是因为输入会立即回显给终端,然后您的程序putchar()也会将其返回.如果要取消输出与输出的关联,还必须转动ECHO标志.您只需将相应的行更改为:

newt.c_lflag &= ~(ICANON | ECHO); 
Run Code Online (Sandbox Code Playgroud)

  • +1虽然tbh,这可能不是原始海报适合的代码级别;) (9认同)
  • 大!那就是我想要的! (2认同)

gol*_*udo 65

在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;
}
Run Code Online (Sandbox Code Playgroud)

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

您可以man stty更好地控制终端行为,具体取决于您希望实现的目标.

  • 使用“系统”是一个坏主意。 (4认同)

Eri*_* J. 8

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

  • `getchar()`不关心ENTER,它只是处理通过stdin传递的任何东西.行缓冲往往是OS /终端定义的行为. (31认同)
  • 尽管如此,"getchar()"并不是"需要你按下回车",而是环境. (9认同)

Dav*_*ley 6

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

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

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


Jon*_*ler 5

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

echo "stty -g $(stty -g)" > restore-sanity
stty cbreak
./your_program
Run Code Online (Sandbox Code Playgroud)

当你对程序感到厌烦时,使用中断来停止它。

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

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


小智 5

我喜欢卢卡斯的回答,但我想对其进行详细说明。在termios.hnamed中有一个内置函数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. [...]
Run Code Online (Sandbox Code Playgroud)

这基本上与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;
Run Code Online (Sandbox Code Playgroud)