如何避免在getchar()中按Enter键仅读取单个字符?

如何避免在getchar()中按Enter键仅读取单个字符?,c,input,character,getchar,unbuffered,C,Input,Character,Getchar,Unbuffered,在下一个代码中: #include <stdio.h> int main(void) { int c; while ((c=getchar())!= EOF) putchar(c); return 0; } 但当我按“a”时,什么也没有发生,我可以写其他字母,只有当我按Enter键时,副本才会出现: 我该怎么做 我在Ubuntu下使用命令cc-o example.c进行编译。默认情况下,c库缓冲输出,直到看到返回。要立即打印结果,请使

在下一个代码中:

#include <stdio.h>

int main(void) {   
  int c;   
  while ((c=getchar())!= EOF)      
    putchar(c); 
  return 0;
}
但当我按“a”时,什么也没有发生,我可以写其他字母,只有当我按Enter键时,副本才会出现:

我该怎么做


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

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

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

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

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

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


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

这取决于您的操作系统,如果您处于类似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;
}

由于您使用的是Unix衍生工具(Ubuntu),这里有一种方法可以做到这一点——不推荐,但它可以工作(只要您能够准确地键入命令):

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

sh restore-sanity
  • “echo”行将当前终端设置保存为一个shell脚本,该脚本将恢复这些设置
  • “stty”行关闭大部分特殊处理(例如,Control-D无效),并在字符可用时立即向程序发送字符。这意味着你不能再编辑你键入的内容了
  • “sh”行将恢复您原来的终端设置

如果“stty sane”能够充分准确地恢复您的设置以满足您的需要,您可以节省开支。“-g”的格式不能跨“stty”的版本移植(因此在Solaris 10上生成的内容在Linux上不起作用,反之亦然),但这个概念在任何地方都适用。“stty-sane”选项不是通用的(但在Linux上)。在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;
}
#包括
#包括
内部主(空){
INTC;
/*使用系统调用使终端将所有击键直接发送到stdin*/
系统(“/bin/stty raw”);
而((c=getchar())!='。){
/*键入一个句点以打破循环,因为CTRL-D不能正常工作*/
普查尔(c);
}
/*使用系统调用将终端行为设置为更正常的行为*/
系统(“/bin/stty”);
返回0;
}
请注意,这并不是最理想的,因为它只是假设
stty-cooked
是程序退出时所需的行为,而不是检查原始终端设置。此外,由于在原始模式下跳过了所有特殊处理,许多键序列(如CTRL-C或CTRL-D)在没有在程序中显式处理的情况下实际上无法按预期工作


您可以
manstty
来获得对终端行为的更多控制,具体取决于您想要实现什么。

您可以包括“ncurses”库,并使用
getch()
而不是
getchar()
是的,您也可以在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;
}
#包括//基本输入/输出
#include//提供非标准的getch()函数
使用名称空间std;
int main()
{  

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

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

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

system( "stty -raw" );

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

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

现在一切都很顺利。

我喜欢Lucas answer,但我想详细说明一下。
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的建议基本相同,而且更多,您可以在手册页中看到它设置的确切标志:

用例
这个代码对我有用。注意:这个
/dev/tty
system( "stty -raw" );
system( "stty -icanon" );
system( "/bin/stty --file=/dev/tty -icanon" );
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. [...]
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;
#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;
}
#include <stdio.h>
#include <conio.h>   

int main (void) 
{
    int c;

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

        printf("\n");
    }

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