从标准输入中捕获字符,无需等待按下输入

Ada*_*dam 163 c c++ inputstream

我永远不会记得我是怎么做的,因为它对我来说很少见.但是在C或C++中,从标准输入读取字符而不等待换行符的最佳方法是什么(按回车键).

理想情况下,它也不会将输入字符回显到屏幕上.我只想捕捉击键而不影响控制台屏幕.

Joh*_*itb 91

这在纯C++中是不可能的,因为它过分依赖于可能与stdin连接的终端(它们通常是行缓冲的).但是,你可以使用一个库:

  1. conio可用于Windows编译器.使用该功能stdin可以在不等待回车键的情况下为您提供角色.我不是常见的Windows开发人员,但我看到我的同学只是包含_getch()并使用它.请参阅维基百科的conio.h.它列出了<conio.h>,在Visual C++中声明已弃用.
  2. curses可用于linux,兼容的curses实现也可用于Windows.它还有一个conio.h功能.(尝试getch()查看其联机帮助页).请参阅维基百科的Curses.

如果您的目标是跨平台兼容性,我建议您使用curses.也就是说,我确信有一些功能可以用来关闭线缓冲(我相信这叫做"原始模式",而不是"煮熟模式"(查看getch())).如果我没弄错的话,诅咒会以便携的方式为你处理.

  • 请注意,现在,`ncurses`是`curses`的推荐变体. (7认同)
  • 如果您确实需要一个图书馆,那么他们是怎么做到的?要确定您必须对该库进行编程,那意味着任何人都可以对其进行编程,那么为什么我们不能对我们自己需要的库代码进行编程呢?这也将使得**在纯c ++中不可能移植** (2认同)
  • @kid8 我不明白 (2认同)
  • @JohannesSchaub-litb,当您说不可能时,他可能想说明库实现者是如何在纯 c/c++ 中制作该可移植方法的。 (2认同)
  • @AbhinavGauniyal 啊,我明白了。但是我并没有说库实现者没有制作可移植的方法(即由合理的可移植程序使用)。我暗示他们没有使用可移植的 C++。显然他们没有,我不明白为什么他的评论说我的这部分答案是错误的。 (2认同)

小智 77

在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);
}
Run Code Online (Sandbox Code Playgroud)

基本上你必须关闭规范模式(和回声模式来抑制回声).

  • 可能是因为这段代码是错误的,因为read()是在unistd.h中定义的POSIX系统调用.stdio.h可能巧合地包含它,但实际上你根本不需要stdio.h; 用unistd.h替换它应该是好的. (6认同)
  • 有没有简单的方法将其扩展到不阻止? (3认同)
  • @FalconMomot在我的NetBeans IDE 8.1(在Kali Linux中)中说:“错误:编译时未在此范围内声明'perror',但在包含`stdio.h`和`unistd.h`时可以正常工作。 (2认同)

cwh*_*iii 17

我在寻找解决同样问题的同时在另一个论坛上找到了这个.我从我发现的东西中修改了一下.它很棒.我正在运行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; 
}
Run Code Online (Sandbox Code Playgroud)

  • 虽然这是有效的,但对于它的价值而言,在我看来,对系统进行外壳很少是"最好"的方法.stty程序是用C语言编写的,所以你可以包含<termios.h>或<sgtty.h>并调用stty使用的相同代码,而不依赖于外部程序/ fork/whatnot. (13认同)

小智 14

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.
Run Code Online (Sandbox Code Playgroud)

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

要么

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

  • `conio.h` 已经过时了,只能用于旧的 DOS API,请不要使用它 (3认同)

has*_*sen 9

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

HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
DWORD events;
INPUT_RECORD buffer;
PeekConsoleInput( handle, &buffer, 1, &events );
Run Code Online (Sandbox Code Playgroud)

然后使用ReadConsoleInput"消耗"输入字符..

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

说实话,这是来自我的一些旧代码,所以你必须弄清楚它.

但很酷的是,它在没有提示任何内容的情况下读取输入,因此根本不显示字符.


Jos*_*tra 9

#include <conio.h>

if (kbhit() != 0) {
    cout << getch() << endl;
}
Run Code Online (Sandbox Code Playgroud)

这用于kbhit()检查键盘是否被按下并用于getch()获取正在按下的字符.

  • [CONIO.H](http://en.wikipedia.org/wiki/Conio.h)?"conio.h是旧的MS-DOS编译器中用于创建文本用户界面的C头文件." 似乎有些过时了. (5认同)

ssi*_*fod 7

我使用 kbhit() 查看是否存在字符,然后使用 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;
}
Run Code Online (Sandbox Code Playgroud)


Pro*_*ysX 7

由于这里以前的解决方案不能跨平台工作并且在使用特殊键时遇到问题,因此这是我的解决方案,它可以在 Windows 和 Linux 上运行,并且使用最少的外部库(对于Windows.hWindows 和sys/ioctl.h+termios.h对于 Linux)。

\n

对于 ASCII 字符(换行符/制表符/空格/退格符/删除符,!"#$%&\'()*+,-./0-9:;<=>?@AZ[]^_`az{|} 〜\ xc3 \ xbc \ xc3 \ xa4 \ xc3 \ x84 \ xc3 \ xb6 \ xc3 \ x96 \ xc3 \ x9c \ xc3 \ x9f \ xc2 \ xb5 \ xc2 \ xb4 \ xc2 \ xa7 \ xc2 \ xb0 \ xc2 \ xb9 \ xc2\xb3\xc2\xb2),返回 ASCII 代码(正数),对于特殊键(箭头键、向上/向下翻页、pos1/end、转义、插入、F1-F12),返回Windows Virtual-的负数返回键码(负数)。

\n
#include <iostream>\n#include <string>\n#include <thread> // contains <chrono>\nusing namespace std;\n\nvoid println(const string& s="") {\n    cout << s << endl;\n}\nvoid sleep(const double t) {\n    if(t>0.0) this_thread::sleep_for(chrono::milliseconds((int)(1E3*t+0.5)));\n}\n\n\n\n// ASCII codes (key>0): 8 backspace, 9 tab, 10 newline, 27 escape, 127 delete, !"#$%&\'()*+,-./0-9:;<=>?@A-Z[]^_`a-z{|}~\xc3\xbc\xc3\xa4\xc3\x84\xc3\xb6\xc3\x96\xc3\x9c\xc3\x9f\xc2\xb5\xc2\xb4\xc2\xa7\xc2\xb0\xc2\xb9\xc2\xb3\xc2\xb2\n// control key codes (key<0): -38/-40/-37/-39 up/down/left/right arrow, -33/-34 page up/down, -36/-35 pos1/end\n// other key codes (key<0): -45 insert, -144 num lock, -20 caps lock, -91 windows key, -93 kontext menu key, -112 to -123 F1 to F12\n// not working: \xc2\xb9 (251), num lock (-144), caps lock (-20), windows key (-91), kontext menu key (-93), F11 (-122)\n#if defined(_WIN32)\n#define WIN32_LEAN_AND_MEAN\n#define VC_EXTRALEAN\n#include <Windows.h>\nint key_press() { // not working: F11 (-122, toggles fullscreen)\n    KEY_EVENT_RECORD keyevent;\n    INPUT_RECORD irec;\n    DWORD events;\n    while(true) {\n        ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &irec, 1, &events);\n        if(irec.EventType==KEY_EVENT&&((KEY_EVENT_RECORD&)irec.Event).bKeyDown) {\n            keyevent = (KEY_EVENT_RECORD&)irec.Event;\n            const int ca = (int)keyevent.uChar.AsciiChar;\n            const int cv = (int)keyevent.wVirtualKeyCode;\n            const int key = ca==0 ? -cv : ca+(ca>0?0:256);\n            switch(key) {\n                case  -16: continue; // disable Shift\n                case  -17: continue; // disable Ctrl / AltGr\n                case  -18: continue; // disable Alt / AltGr\n                case -220: continue; // disable first detection of "^" key (not "^" symbol)\n                case -221: continue; // disable first detection of "`" key (not "`" symbol)\n                case -191: continue; // disable AltGr + "#"\n                case  -52: continue; // disable AltGr + "4"\n                case  -53: continue; // disable AltGr + "5"\n                case  -54: continue; // disable AltGr + "6"\n                case  -12: continue; // disable num block 5 with num lock deactivated\n                case   13: return  10; // enter\n                case  -46: return 127; // delete\n                case  -49: return 251; // \xc2\xb9\n                case    0: continue;\n                case    1: continue; // disable Ctrl + a (selects all text)\n                case    2: continue; // disable Ctrl + b\n                case    3: continue; // disable Ctrl + c (terminates program)\n                case    4: continue; // disable Ctrl + d\n                case    5: continue; // disable Ctrl + e\n                case    6: continue; // disable Ctrl + f (opens search)\n                case    7: continue; // disable Ctrl + g\n                //case    8: continue; // disable Ctrl + h (ascii for backspace)\n                //case    9: continue; // disable Ctrl + i (ascii for tab)\n                case   10: continue; // disable Ctrl + j\n                case   11: continue; // disable Ctrl + k\n                case   12: continue; // disable Ctrl + l\n                //case   13: continue; // disable Ctrl + m (breaks console, ascii for new line)\n                case   14: continue; // disable Ctrl + n\n                case   15: continue; // disable Ctrl + o\n                case   16: continue; // disable Ctrl + p\n                case   17: continue; // disable Ctrl + q\n                case   18: continue; // disable Ctrl + r\n                case   19: continue; // disable Ctrl + s\n                case   20: continue; // disable Ctrl + t\n                case   21: continue; // disable Ctrl + u\n                case   22: continue; // disable Ctrl + v (inserts clipboard)\n                case   23: continue; // disable Ctrl + w\n                case   24: continue; // disable Ctrl + x\n                case   25: continue; // disable Ctrl + y\n                case   26: continue; // disable Ctrl + z\n                default: return key; // any other ASCII/virtual character\n            }\n        }\n    }\n}\n#elif defined(__linux__)\n#include <sys/ioctl.h>\n#include <termios.h>\nint key_press() { // not working: \xc2\xb9 (251), num lock (-144), caps lock (-20), windows key (-91), kontext menu key (-93)\n    struct termios term;\n    tcgetattr(0, &term);\n    while(true) {\n        term.c_lflag &= ~(ICANON|ECHO); // turn off line buffering and echoing\n        tcsetattr(0, TCSANOW, &term);\n        int nbbytes;\n        ioctl(0, FIONREAD, &nbbytes); // 0 is STDIN\n        while(!nbbytes) {\n            sleep(0.01);\n            fflush(stdout);\n            ioctl(0, FIONREAD, &nbbytes); // 0 is STDIN\n        }\n        int key = (int)getchar();\n        if(key==27||key==194||key==195) { // escape, 194/195 is escape for \xc2\xb0\xc3\x9f\xc2\xb4\xc3\xa4\xc3\xb6\xc3\xbc\xc3\x84\xc3\x96\xc3\x9c\n            key = (int)getchar();\n            if(key==91) { // [ following escape\n                key = (int)getchar(); // get code of next char after \\e[\n                if(key==49) { // F5-F8\n                    key = 62+(int)getchar(); // 53, 55-57\n                    if(key==115) key++; // F5 code is too low by 1\n                    getchar(); // take in following ~ (126), but discard code\n                } else if(key==50) { // insert or F9-F12\n                    key = (int)getchar();\n                    if(key==126) { // insert\n                        key = 45;\n                    } else { // F9-F12\n                        key += 71; // 48, 49, 51, 52\n                        if(key<121) key++; // F11 and F12 are too low by 1\n                        getchar(); // take in following ~ (126), but discard code\n                    }\n                } else if(key==51||key==53||key==54) { // delete, page up/down\n                    getchar(); // take in following ~ (126), but discard code\n                }\n            } else if(key==79) { // F1-F4\n                key = 32+(int)getchar(); // 80-83\n            }\n            key = -key; // use negative numbers for escaped keys\n        }\n        term.c_lflag |= (ICANON|ECHO); // turn on line buffering and echoing\n        tcsetattr(0, TCSANOW, &term);\n        switch(key) {\n            case  127: return   8; // backspace\n            case  -27: return  27; // escape\n            case  -51: return 127; // delete\n            case -164: return 132; // \xc3\xa4\n            case -182: return 148; // \xc3\xb6\n            case -188: return 129; // \xc3\xbc\n            case -132: return 142; // \xc3\x84\n            case -150: return 153; // \xc3\x96\n            case -156: return 154; // \xc3\x9c\n            case -159: return 225; // \xc3\x9f\n            case -181: return 230; // \xc2\xb5\n            case -167: return 245; // \xc2\xa7\n            case -176: return 248; // \xc2\xb0\n            case -178: return 253; // \xc2\xb2\n            case -179: return 252; // \xc2\xb3\n            case -180: return 239; // \xc2\xb4\n            case  -65: return -38; // up arrow\n            case  -66: return -40; // down arrow\n            case  -68: return -37; // left arrow\n            case  -67: return -39; // right arrow\n            case  -53: return -33; // page up\n            case  -54: return -34; // page down\n            case  -72: return -36; // pos1\n            case  -70: return -35; // end\n            case    0: continue;\n            case    1: continue; // disable Ctrl + a\n            case    2: continue; // disable Ctrl + b\n            case    3: continue; // disable Ctrl + c (terminates program)\n            case    4: continue; // disable Ctrl + d\n            case    5: continue; // disable Ctrl + e\n            case    6: continue; // disable Ctrl + f\n            case    7: continue; // disable Ctrl + g\n            case    8: continue; // disable Ctrl + h\n            //case    9: continue; // disable Ctrl + i (ascii for tab)\n            //case   10: continue; // disable Ctrl + j (ascii for new line)\n            case   11: continue; // disable Ctrl + k\n            case   12: continue; // disable Ctrl + l\n            case   13: continue; // disable Ctrl + m\n            case   14: continue; // disable Ctrl + n\n            case   15: continue; // disable Ctrl + o\n            case   16: continue; // disable Ctrl + p\n            case   17: continue; // disable Ctrl + q\n            case   18: continue; // disable Ctrl + r\n            case   19: continue; // disable Ctrl + s\n            case   20: continue; // disable Ctrl + t\n            case   21: continue; // disable Ctrl + u\n            case   22: continue; // disable Ctrl + v\n            case   23: continue; // disable Ctrl + w\n            case   24: continue; // disable Ctrl + x\n            case   25: continue; // disable Ctrl + y\n            case   26: continue; // disable Ctrl + z (terminates program)\n            default: return key; // any other ASCII character\n        }\n    }\n}\n#endif // Windows/Linux\n
Run Code Online (Sandbox Code Playgroud)\n

最后,这是一个如何使用它的示例:

\n
int main() {\n    while(true) {\n        const int key = key_press(); // blocks until a key is pressed\n        println("Input is: "+to_string(key)+", \\""+(char)key+"\\"");\n    }\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n


Ang*_*ane 6

ncurses 提供了一个很好的方法来做到这一点!这也是我的第一篇文章(我记得),所以欢迎任何评论。我会欣赏有用的,但欢迎所有人!

编译: g++ -std=c++11 -pthread -lncurses .cpp -o

#include <iostream>
#include <ncurses.h>
#include <future>

char get_keyboard_input();

int main(int argc, char *argv[])
{
    initscr();
    raw();
    noecho();
    keypad(stdscr,true);

    auto f = std::async(std::launch::async, get_keyboard_input);
    while (f.wait_for(std::chrono::milliseconds(20)) != std::future_status::ready)
    {
        // do some work
    }

    endwin();
    std::cout << "returned: " << f.get() << std::endl;
    return 0;
}

char get_keyboard_input()
{
    char input = '0';
    while(input != 'q')
    {
        input = getch();
    }
    return input;
}
Run Code Online (Sandbox Code Playgroud)


小智 5

假设Windows,请查看ReadConsoleInput函数.


Dav*_*ley 5

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

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