在下一个代码中:
#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
进行编译。
答案 0 :(得分:77)
这取决于您的操作系统,如果您在类似UNIX的环境中默认启用ICANON标志,那么输入将缓冲到下一个'\n'
或EOF
。通过禁用规范模式,您将立即获得字符。这在其他平台上也是可行的,但没有直接的跨平台解决方案。
#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
如果'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).
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);
}