从标准input中捕获字符而不必等待input被按下

我永远不会记得我是如何这样做的,因为它对我来说很less出现。 但在C或C ++中,从标准input中读取字符而不等待换行(按Enter)的最佳方式是什么。

也理想情况下,它不会回应input字符到屏幕上。 我只想捕获击出控制台屏幕的击键。

这在纯粹的C ++中是不可能移植的,因为它太依赖于可能与stdin连接的terminal(它们通常是线路缓冲的)。 你可以,但是使用一个库:

  1. conio可用于Windows编译器。 使用函数_getch()为您提供一个字符,而不必等待input键。 我不是一个频繁的Windows开发人员,但我看到我的同学只是包括conio.h并使用它。 请参阅wikipedia上的conio.h 。 它列出了getch ,它在Visual C ++中声明为不赞成使用。
  2. curses可用于Linux,兼容的curses实现也可用于Windows。 它也有一个getch函数。 (尝试man getch查看其手册页)。 请参阅维基百科的诅咒 。

如果你的目标是跨平台的兼容性,我会build议你使用curses。 这就是说,我确定有一些函数可以用来closures行缓冲(我相信这就是所谓的“原始模式”,而不是“熟化模式”(look into man stty ))。 如果我没有弄错的话,诅咒就会以便携的方式处理你。

在Linux(和其他类Unix系统)上,可以通过以下方式完成:

 #include <unistd.h> #include <termios.h> char getch() { char buf = 0; struct termios old = {0}; if (tcgetattr(0, &old) < 0) perror("tcsetattr()"); old.c_lflag &= ~ICANON; old.c_lflag &= ~ECHO; old.c_cc[VMIN] = 1; old.c_cc[VTIME] = 0; if (tcsetattr(0, TCSANOW, &old) < 0) perror("tcsetattr ICANON"); if (read(0, &buf, 1) < 0) perror ("read()"); old.c_lflag |= ICANON; old.c_lflag |= ECHO; if (tcsetattr(0, TCSADRAIN, &old) < 0) perror ("tcsetattr ~ICANON"); return (buf); } 

基本上你必须closures规范模式(和回声模式来抑制回声)。

CONIO.H

你需要的function是:

 int getch(); Prototype int _getch(void); Description _getch obtains a character from stdin. Input is unbuffered, and this routine will return as soon as a character is available without waiting for a carriage return. The character is not echoed to stdout. _getch bypasses the normal buffering done by getchar and getc. ungetc cannot be used with _getch. Synonym Function: getch int kbhit(); Description Checks if a keyboard key has been pressed but not yet read. Return Value Returns a non-zero value if a key was pressed. Otherwise, returns 0. 

libconio http://sourceforge.net/projects/libconio

要么

Linux的c ++实现conio.h http://sourceforge.net/projects/linux-conioh

我在另一个论坛上发现了这个问题,同时寻求解决同样的问题。 我已经修改了一些我发现的。 它工作很好。 我正在运行OS X,所以如果你运行的是Microsoft,你需要find正确的system()命令切换到raw和cooked模式。

 #include <iostream> #include <stdio.h> using namespace std; int main() { // Output prompt cout << "Press any key to continue..." << endl; // Set terminal to raw mode system("stty raw"); // Wait for single character char input = getchar(); // Echo input: cout << "--" << input << "--"; // Reset terminal to normal "cooked" mode system("stty cooked"); // And we're out of here return 0; } 
 #include <conio.h> if (kbhit() != 0) { cout << getch() << endl; } 

这使用kbhit()来检查键盘是否被按下,并使用getch()来获取正在按下的字符。

如果你在windows上,你可以使用PeekConsoleInput来检测是否有任何input,

 HANDLE handle = GetStdHandle(STD_INPUT_HANDLE); DWORD events; INPUT_RECORD buffer; PeekConsoleInput( handle, &buffer, 1, &events ); 

然后使用ReadConsoleInput来“消耗”input字符。

 PeekConsoleInput(handle, &buffer, 1, &events); if(events > 0) { ReadConsoleInput(handle, &buffer, 1, &events); return buffer.Event.KeyEvent.wVirtualKeyCode; } else return 0 

说实话,这是从我的一些旧的代码,所以你必须摆弄一下。

不过很酷的是它读取input时不会提示任何内容,所以字符根本不显示。

假设Windows,看看ReadConsoleInput函数。

最接近便携的是使用ncurses库将terminal置于“cbreak模式”。 API是巨大的; 你最想要的例程是

  • initscrendwin
  • cbreaknocbreak
  • getch

祝你好运!

以下是从Expert C Programming:Deep Secrets中提取的一个解决scheme,它应该在SVr4上运行。 它使用sttyioctl

 #include <sys/filio.h> int kbhit() { int i; ioctl(0, FIONREAD, &i); return i; /* return a count of chars available to read */ } main() { int i = 0; intc=''; system("stty raw -echo"); printf("enter 'q' to quit \n"); for (;c!='q';i++) { if (kbhit()) { c=getchar(); printf("\n got %c, on iteration %d",c, i); } } system("stty cooked echo"); } 

C和C ++对I / O采取非常抽象的观点,没有标准的做法。 有标准的方法可以从标准inputstream中获取字符,如果有任何可以获得的字符,其他语言都没有定义。 因此,任何答案都必须是平台特定的,不仅可能依赖于操作系统,还可能依赖于软件框架。

这里有一些合理的猜测,但如果不知道你的目标环境是什么,就无法回答你的问题。

我一直想要一个循环来读取我的input,而不按回车键。 这对我工作。

 #include<stdio.h> main() { char ch; system("stty raw");//seting the terminal in raw mode while(1) { ch=getchar(); if(ch=='~'){ //terminate or come out of raw mode on "~" pressed system("stty cooked"); //while(1);//you may still run the code exit(0); //or terminate } printf("you pressed %c\n ",ch); //write rest code here } } 

为我在Windows上工作:

 #include <conio.h> char c = _getch(); 

我使用kbhit()来查看是否存在char,然后使用getchar()来读取数据。 在Windows上,你可以使用“conio.h”。 在Linux上,你将不得不实现你自己的kbhit()。

见下面的代码:

 // kbhit #include <stdio.h> #include <sys/ioctl.h> // For FIONREAD #include <termios.h> #include <stdbool.h> int kbhit(void) { static bool initflag = false; static const int STDIN = 0; if (!initflag) { // Use termios to turn off line buffering struct termios term; tcgetattr(STDIN, &term); term.c_lflag &= ~ICANON; tcsetattr(STDIN, TCSANOW, &term); setbuf(stdin, NULL); initflag = true; } int nbbytes; ioctl(STDIN, FIONREAD, &nbbytes); // 0 is STDIN return nbbytes; } // main #include <unistd.h> int main(int argc, char** argv) { char c; //setbuf(stdout, NULL); // Optional: No buffering. //setbuf(stdin, NULL); // Optional: No buffering. printf("Press key"); while (!kbhit()) { printf("."); fflush(stdout); sleep(1); } c = getchar(); printf("\nChar received:%c\n", c); printf("Done.\n"); return 0; } 

你可以使用SDL(Simple DirectMedia Library)来移植它,尽pipe我怀疑你可能不喜欢它的行为。 当我尝试它时,我不得不让SDL创build一个新的video窗口(尽pipe我不需要它用于我的程序),并有这个窗口“抓住”几乎所有的键盘和鼠标input(这可以用于我的使用,但可以在其他情况下令人讨厌或不可行)。 我怀疑这是过分的和不值得的,除非完全的可移植性是必须的 – 否则尝试其他build议的解决scheme之一。

顺便说一下,如果你进入这个环境,这将会给你分别的关键新闻和发布活动。