2009-01-07 48 views
128

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

理想情况下,它不会将输入字符回显到屏幕。我只想捕获击出控制台屏幕的击键。

+1

你在Windows或不呢? – hasen 2009-01-08 00:55:46

+1

@adam - 你能否澄清一下:你是否想要一个函数,如果没有可用的字符,它会立即返回,或者一直等待一个按键? – Roddy 2009-01-08 09:47:30

+1

@Roddy - 我想要一个总是等待一个按键的功能。 – Adam 2009-05-28 04:52:54

回答

80

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

  1. conio可用于Windows编译器。使用功能_getch()为您提供一个角色,而无需等待输入密钥。我不是一个频繁的Windows开发人员,但我看到我的同学只包括conio.h并使用它。请参阅维基百科的conio.h。它列出了getch,它在Visual C++中声明为不赞成使用。
  2. curses可用于Linux,兼容curses实现也可用于Windows。它也有一个getch函数。 (尝试man getch查看其联机帮助页)。请参阅维基百科的Curses

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

+6

请注意,现在`ncurses`是'curses`的推荐变种。 – 2016-05-07 03:04:01

+0

如果你确实需要一个图书馆,那他们是怎么做到的?为了使图书馆你一定要编程它,这意味着任何人都可以编程,所以为什么我们不能编程我们自己需要的库代码?这也将使**这不可能在纯粹的C + +错误 – NathanProgrammer 2017-01-03 09:49:00

+0

@ kid8我不明白 – 2017-01-03 16:51:24

5

假设Windows,看看ReadConsoleInput函数。

3

C和C++采用非常抽象的I/O视图,并且没有标准的做你想做的方式。有标准的方法可以从标准输入流中获取字符,如果有任何可以获得的字符,其他任何语言都不会定义其他字符。因此,任何答案都必须是平台特定的,可能不仅取决于操作系统,还取决于软件框架。

这里有一些合理的猜测,但没有办法在不知道目标环境是什么的情况下回答你的问题。

15

CONIO.H

您需要的功能是:

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

4

与便携式设备最接近的是使用ncurses库将终端设置为“cbreak模式”。API是巨大的;你最想要的程序是

  • initscrendwin
  • cbreaknocbreak
  • getch

祝你好运!

7

如果您使用的是Windows,你可以使用PeekConsoleInput来检测是否有任何输入,

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

然后使用ReadConsoleInput“消费”输入字符..

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

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

虽然很酷的事情是它读取输入时没有提示任何内容,所以字符根本不显示。

4

以下是从提取的解决方案专家C编程:深度秘密,它应该在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"); 
} 
13

我发现这在另一个论坛上,同时寻找解决同样的问题。我从我发现的内容中修改了一下。它效果很好。我正在运行OS X,因此如果您运行的是Microsoft,则需要找到正确的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; 
} 
71

在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); 
} 

基本上你必须关闭典型模式(和回声模式,以抑制呼应)。

8
#include <conio.h> 

if (kbhit() != 0) { 
    cout << getch() << endl; 
} 

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

1

您可以使用SDL(Simple DirectMedia Library)轻松转换,尽管我怀疑您可能不喜欢它的行为。当我尝试它时,我不得不让SDL创建一个新的视频窗口(尽管我不需要它用于我的程序),并让此窗口“抓住”几乎所有的键盘和鼠标输入(这对我的使用来说是可以的,但可以在其他情况下令人讨厌或不可行)。我怀疑它是过度的,不值得这样做,除非完全的可移植性是必须的 - 否则尝试其他建议的解决方案之一。

顺便说一句,如果你喜欢这样,这将分别为你提供按键和发布事件。

2

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

#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 
     } 

    } 
2

为我工作在Windows上:

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

我用的kbhit(),看是否有烧焦存在,然后的getchar()读取数据。 在窗口上,您可以使用“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; 
}