我从来都不记得我是怎么做到的,因为它很少出现在我身上。但是在C或c++中,从标准输入中读取字符而不等待换行符(按enter)的最佳方法是什么?

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


当前回答

#include <conio.h>

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

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

其他回答

You can do it portably using SDL (the Simple DirectMedia Library), though I suspect you may not like its behavior. When I tried it, I had to have SDL create a new video window (even though I didn't need it for my program) and have this window "grab" almost all keyboard and mouse input (which was okay for my usage but could be annoying or unworkable in other situations). I suspect it's overkill and not worth it unless complete portability is a must--otherwise try one of the other suggested solutions.

顺便说一下,这将分别提供按键和释放事件,如果你喜欢的话。

我在另一个论坛上发现了这个,当时我想解决同样的问题。根据我的发现,我做了一些修改。效果很好。我运行的是OS X,所以如果你运行的是微软,你需要找到正确的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; 
}

这在纯c++中以可移植的方式是不可能的,因为它太依赖于可能与stdin连接的终端(它们通常是行缓冲的)。不过,你可以使用一个库来实现:

conio available with Windows compilers. Use the _getch() function to give you a character without waiting for the Enter key. I'm not a frequent Windows developer, but I've seen my classmates just include <conio.h> and use it. See conio.h at Wikipedia. It lists getch(), which is declared deprecated in Visual C++. curses available for Linux. Compatible curses implementations are available for Windows too. It has also a getch() function. (try man getch to view its manpage). See Curses at Wikipedia.

如果你的目标是跨平台兼容性,我建议你使用curses。也就是说,我相信有一些函数可以用来关闭行缓冲(我相信这被称为“原始模式”,而不是“熟模式”-看看man stty)。如果我没记错的话,诅咒可以方便地帮你解决这个问题。

下面是一个不向系统提供外壳的版本(在macOS 10.14上编写和测试)

#include <unistd.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>

char* getStr( char* buffer , int maxRead ) {
  int  numRead  = 0;
  char ch;

  struct termios old = {0};
  struct termios new = {0};
  if( tcgetattr( 0 , &old ) < 0 )        perror( "tcgetattr() old settings" );
  if( tcgetattr( 0 , &new ) < 0 )        perror( "tcgetaart() new settings" );
  cfmakeraw( &new );
  if( tcsetattr( 0 , TCSADRAIN , &new ) < 0 ) perror( "tcssetattr makeraw new" );

  for( int i = 0 ; i < maxRead ; i++)  {
    ch = getchar();
    switch( ch )  {
      case EOF: 
      case '\n':
      case '\r':
        goto exit_getStr;
        break;

      default:
        printf( "%1c" , ch );
        buffer[ numRead++ ] = ch;
        if( numRead >= maxRead )  {
          goto exit_getStr;
        }
        break;
    }
  }

exit_getStr:
  if( tcsetattr( 0 , TCSADRAIN , &old) < 0)   perror ("tcsetattr reset to old" );
  printf( "\n" );   
  return buffer;
}


int main( void ) 
{
  const int maxChars = 20;
  char      stringBuffer[ maxChars+1 ];
  memset(   stringBuffer , 0 , maxChars+1 ); // initialize to 0

  printf( "enter a string: ");
  getStr( stringBuffer , maxChars );
  printf( "you entered: [%s]\n" , stringBuffer );
}

由于这里之前的解决方案不能跨平台工作,并且在特殊键上存在问题,所以这里是我的解决方案,它在Windows和Linux上都可以工作,并且使用了最少的外部库(Windows.h和Linux的sys/ioctl.h+termios.h)。

对于ASCII字符(换行符/tab/space/backspace/delete, !"#$%&'()*+,-./0-9:;<=>?@A-Z[]^_ ' a-z{|}~üäÄöÖÜßµ´§°¹³),返回ASCII码(正数);对于特殊键(方向键、上页/下页、pos1/end、转义、插入、F1-F12),返回Windows虚拟键码(负数)的负数。

#include <iostream>
#include <string>
#include <thread> // contains <chrono>
using namespace std;

void println(const string& s="") {
    cout << s << endl;
}
void sleep(const double t) {
    if(t>0.0) this_thread::sleep_for(chrono::milliseconds((int)(1E3*t+0.5)));
}



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

最后,这里有一个如何使用它的例子:

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