Захватывать символы со стандартного ввода, не дожидаясь нажатия клавиши ввода - PullRequest
150 голосов
/ 07 января 2009

Я никогда не могу вспомнить, как я это делаю, потому что это случается так редко для меня. Но в C или C ++, как лучше всего читать символ из стандартного ввода, не дожидаясь перевода строки (нажмите ввод).

Кроме того, в идеале это не будет отображать вводимый символ на экране. Я просто хочу снимать нажатия клавиш, не влияя на экран консоли.

Ответы [ 15 ]

87 голосов
/ 07 января 2009

Это не возможно переносимо в чистом C ++, потому что это слишком сильно зависит от используемого терминала, который может быть подключен к stdin (обычно они буферизуются в строке). Однако вы можете использовать библиотеку для этого:

  1. Conio доступны с Windows компиляторов. Используйте функцию _getch(), чтобы дать вам символ, не дожидаясь клавиши ввода. Я не частый разработчик Windows, но я видел, что мои одноклассники просто включают conio.h и используют его. См. conio.h в Википедии. В нем перечислены getch, которые объявлены устаревшими в Visual C ++.
  2. curses доступны для Linux, совместимые реализации curses доступны и для Windows. Он также имеет функцию getch. (попробуйте man getch, чтобы просмотреть его справочную страницу). См. Проклятия в Википедии.

Я бы порекомендовал вам использовать curses, если вы стремитесь к кроссплатформенной совместимости. Тем не менее, я уверен, что есть функции, которые вы можете использовать для отключения буферизации линии (я полагаю, это называется «сырой режим», а не «приготовленный режим» (см. man stty)). Проклятия справились бы с тобой портативно, если я не ошибаюсь.

76 голосов
/ 27 мая 2009

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

В основном вы должны отключить канонический режим (и режим эха, чтобы подавить эхо).

16 голосов
/ 26 мая 2009

Я нашел это на другом форуме, пытаясь решить ту же проблему. Я немного изменил это из того, что нашел. Работает отлично. Я использую OS X, поэтому, если вы работаете с Microsoft, вам нужно найти правильную команду system (), чтобы переключиться в сырой и готовый режимы.

#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; 
}
14 голосов
/ 08 января 2009

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

или

Реализация conio.h в Linux c ++ http://sourceforge.net/projects/linux-conioh

8 голосов
/ 08 июля 2012
#include <conio.h>

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

Используется kbhit() для проверки нажатия клавиатуры и getch() для получения нажимаемого символа.

7 голосов
/ 08 января 2009

Если вы находитесь в 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

Если честно, это из какого-то старого кода, который у меня есть, так что вам придется немного поиграться с ним.

Крутая вещь заключается в том, что он считывает ввод без запроса чего-либо, поэтому символы вообще не отображаются.

5 голосов
/ 07 января 2009

C и C ++ имеют очень абстрактное представление о вводе / выводе, и не существует стандартного способа делать то, что вы хотите. Существуют стандартные способы получения символов из стандартного входного потока, если они есть, и ни один другой язык не определяет ничего. Поэтому любой ответ должен быть привязан к платформе, возможно, в зависимости не только от операционной системы, но и от структуры программного обеспечения.

Здесь есть некоторые разумные догадки, но нет способа ответить на ваш вопрос, не зная, какова ваша целевая среда.

5 голосов
/ 07 января 2009

Предполагая Windows, взгляните на функцию ReadConsoleInput .

4 голосов
/ 18 октября 2015

Я использую 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;
}
4 голосов
/ 08 января 2009

Самое близкое к портативному - использовать библиотеку ncurses, чтобы перевести терминал в «режим cbreak». API гигантский; подпрограммы, которые вы хотите больше всего,

  • initscr и endwin
  • cbreak и nocbreak
  • getch

Удачи!

...