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

时间:2009-11-25 17:19:59

标签: c getchar

在下一个代码中:

#include <stdio.h>

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

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

aabbccddeeff.....

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

abcdef
abcdef

我该怎么做?

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

13 个答案:

答案 0 :(得分:77)

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

答案 1 :(得分:60)

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

从程序本身改变行为的快速,肮脏且不太特别便携的示例:

#include<stdio.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-C CTRL-D )实际上不会像您期望的那样工作没有在程序中明确处理它们。

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

答案 2 :(得分:7)

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

答案 3 :(得分:6)

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

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

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

答案 4 :(得分:4)

由于您正在使用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上)。

答案 5 :(得分:3)

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;

答案 6 :(得分:2)

您可以添加'ncurses'库,并使用getch()代替getchar()

答案 7 :(得分:1)

是的,您也可以在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;
}

答案 8 :(得分:1)

我已经将这个问题/问题出现在我正在进行的作业中。 它还取决于你从哪个输入中获取。 我正在使用

/dev/tty

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

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

system( "stty -raw" );

system( "stty -icanon" );

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

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

现在一切都是copacetic。

答案 9 :(得分:0)

此代码对我有用。注意:即使大多数编译器(我使用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;
}

此代码检测到第一次按键。

答案 10 :(得分:0)

如何避免用getchar()按下 Enter

首先,终端输入通常是线路输入或完全缓冲的。这意味着操作系统会将来自终端的实际输入存储到缓冲区中。通常,在f.e.时,此缓冲区将刷新到程序中。 \n中已信号通知/提供了stdin。这是按下 Enter

getchar()就在链的尽头。它没有能力真正影响缓冲过程。


我该怎么做?

抛弃getchar() ,如果您不想使用特定的系统调用来显式更改终端的行为,如其他答案中所述。

不幸的是,没有标准的库函数,也没有可移植的方式在单字符输入时刷新缓冲区。但是,存在基于实现的非便携式解决方案。


在Windows / MS-DOS中,getch()头文件中有getche()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中,获得直接字符处理和输出的一种方法是在ncurses-library中使用cbreak()echo()选项以及getch()refresh()例程

请注意,您需要使用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。

答案 11 :(得分:0)

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

ymd

答案 12 :(得分:-1)

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

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