Захватывать символы со стандартного ввода, не дожидаясь нажатия клавиши ввода

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

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

21 ответ

Решение

Это невозможно сделать в чистом 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)). Проклятия справились бы с тобой портативно, если я не ошибаюсь.

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

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

Я нашел это на другом форуме, пытаясь решить ту же проблему. Я немного изменил это из того, что нашел. Работает отлично. Я использую 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; 
}

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 con ++ реализация conio.h http://sourceforge.net/projects/linux-conioh

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

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

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

#include <conio.h>

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

Это использует kbhit() проверить, нажата ли клавиатура и использует ли она 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;
}

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

Поскольку предыдущие решения здесь не работают на разных платформах и имеют проблемы со специальными ключами, вот мое решение, которое работает как в Windows, так и в Linux и использует минимум внешних библиотек ( Windows.h для Windows и sys/ioctl.h+ termios.h для Linux).

Для символов ASCII ( новая строка / табуляция / пробел / возврат / удаление,!"#$%&'()*+,-. / 0-9:;<=>?@AZ[]^_`az{|}~üäÄöÖÜßµ´§°¹³²) возвращаются коды ASCII (положительные числа), а для специальных клавиш (клавиши со стрелками, страница вверх / вниз, pos1 / end, escape, insert) - отрицательные коды виртуальных клавиш 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 top/bottom/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: ´ (239), ¹ (251), F3 (-114), F9 to F12 (-120 to -123), num lock (-144), caps lock (-20), windows key (-91), kontext menu key (-93)
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#include <Windows.h>
int key_press() { // not working: ´ (239), F3 (code -114 identical to Ä), 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;
            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 -124: return 132; // ä
                case -108: return 148; // ö
                case -127: return 129; // ü
                case -114: return 142; // Ä
                case -103: return 153; // Ö
                case -102: return 154; // Ü
                case  -31: return 225; // ß
                case  -26: return 230; // µ
                case  -11: return 245; // §
                case   -8: return 248; // °
                case  -49: return 251; // ¹
                case   -3: return 253; // ²
                case   -4: return 252; // ³
                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>
void terminal_init() {
    struct termios term;
    tcgetattr(0, &term);
    term.c_lflag &= ~ICANON; // turn off line buffering
    term.c_lflag &= ~ECHO; // turn off echoing
    tcsetattr(0, TCSANOW, &term);
}
void terminal_exit() {
    struct termios term;
    tcgetattr(0, &term);
    term.c_lflag |= ICANON; // turn on line buffering
    term.c_lflag |= ECHO; // turn on echoing
    tcsetattr(0, TCSANOW, &term);
}
int kbhit() {
    int nbbytes;
    ioctl(0, FIONREAD, &nbbytes); // 0 is STDIN
    return nbbytes;
}
int key_press() { // not working: ¹ (251), num lock (-144), caps lock (-20), windows key (-91), kontext menu key (-93), F9 to F12 (-120 to 123)
    while(true) {
        terminal_init();
        while(!kbhit()) {
            fflush(stdout);
            sleep(0.01);
        }
        int key = (int)getchar();
        if(key==27||key==194||key==195) { // escape
            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==79) { // F1-F4
                key = 32+(int)getchar(); // 80-83
            }
            if(key==51||key==53||key==54||key==50) { // delete, page up/down, insert
                getchar(); // take in following ~ (126), but discard code
            }
            key = -key; // use negative numbers for escaped keys
        }
        terminal_exit();
        switch(key) {
            case  127: return   8; // backspace
            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; // top arrow
            case  -66: return -40; // bottom 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  -50: return -45; // insert
            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 (ascii for backspace)
            //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(int argc, char* argv[]) {
    while(true) {
        const int key = key_press();
        println("Input is: "+to_string(key)+", \""+(char)key+"\"");
    }
    return 0;
}

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

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

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

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

  • initscr а также endwin
  • cbreak а также nocbreak
  • getch

Удачи!

Ниже приведено решение, извлеченное из Expert C Programming: Deep Secrets, которое должно работать на SVr4. Он использует stty и ioctl.

#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");
}

У меня работает на windows:

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

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

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

    }

Вы можете сделать это мобильно, используя SDL (Simple DirectMedia Library), хотя я подозреваю, что вам может не понравиться его поведение. Когда я попробовал это, мне нужно было, чтобы SDL создал новое видеоокно (хотя оно мне и не понадобилось для моей программы), и чтобы это окно "захватывало" почти весь ввод с клавиатуры и мыши (что было хорошо для моего использования, но могло быть раздражающим или неработающим в других ситуациях). Я подозреваю, что это излишне и не стоит, если полная мобильность не является обязательной - в противном случае попробуйте одно из других предложенных решений.

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

Я столкнулся с этой же проблемой. Вот небольшое решение для консоли Windows с использованием cygwin g++ с if(GetKeyState(keycode) & bitANDcompare){};.

      #include <windows.h>
#include <fstream>
#include <iostream>

using namespace std;
void clear() {
    COORD topLeft  = { 0, 0 };
    HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_SCREEN_BUFFER_INFO screen;
    DWORD written;

    GetConsoleScreenBufferInfo(console, &screen);
    FillConsoleOutputCharacterA(
        console, ' ', screen.dwSize.X * screen.dwSize.Y, topLeft, &written
    );
    FillConsoleOutputAttribute(
        console, FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE,
        screen.dwSize.X * screen.dwSize.Y, topLeft, &written
    );
    SetConsoleCursorPosition(console, topLeft);
}

class Keyclick{
    private:
    int key;
    char id;
    public:
    bool keydown = false;
    Keyclick(int key1, char id1){
        key=key1;
        id=id1;
    };
    void watch(){
        if(keydown==false){
            if(GetKeyState(key) & 0x8000 ){
                cout << id;
                cout << "  pressed.\r\n";
                keydown = true;
            }
        }
        if(keydown == true){
            if(!(GetKeyState(key) & 0x8000)) {
                cout << "released!!!!!!!!!!\r\n";
                keydown = false;
                clear();
            }
        }
    };
};

int main()
{
    bool primaryloop =true;
    Keyclick keysp(VK_SPACE,'S');
    Keyclick keyw(0x57,'w');
    Keyclick keya(0x41,'a');
    Keyclick keys(0x53,'s');
    Keyclick keyd(0x44,'d');
    Keyclick keyesc(VK_ESCAPE,'E');
    
    while(primaryloop){
        keysp.watch();
        keyw.watch();
        keya.watch();
        keys.watch();
        keyd.watch();
        keyesc.watch();
        
        if(keyesc.keydown){
            primaryloop=false;
        };      
    }
    return 0;
}

https://github.com/wark77/windows_console_keypoller/blob/main/getkeystate_SOLUTION01.cpp

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

Для этого в Linux вы можете использовать следующую команду bash:

      read -sn1 c && printf "You Pressed: %s\n" "$c"

См. мой ответ здесь для получения подробной информации об этом: .

Итак, чтобы сделать это на C или C++ в Linux, вам просто нужно вызвать вышеуказанную команду bash через системный вызов через канал с и fgets()так что вы можете прочитать вывод команды bash.

Вот полный пример, который отлично работает в Linux как на C, так и на C++:

read_keypress_system_call.c:

      #include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
#include <stdint.h>  // For `uint8_t`, `int8_t`, etc.
#include <stdio.h>   // For `printf()`
#include <stdlib.h>

#define BUFSIZE 32

// Read a keyboard key press and return the character pressed, or a negative
// number in the event of an error.
// NB: for help reading output from system calls, see here:
//  1. https://stackoverflow.com/a/28971647/4561887
//  2. https://stackoverflow.com/a/18297075/4561887
char getKeypress()
{
    // This bash cmd is from my answer here:
    // https://stackoverflow.com/a/70979348/4561887
    const char* cmd = "bash -c 'read -s -n1 c && printf \"%s\" \"$c\"'";
    FILE *fp = popen(cmd, "r");
    if (fp == NULL)
    {
        printf("\nError opening pipe!\n");
        return -1;
    }

    char buf[BUFSIZE] = {0};
    char* retval1 = fgets(buf, BUFSIZE, fp);
    if (retval1 == NULL)
    {
        printf("\nFailed to read cmd response.\n");
        return -2;
    }

    // See meaning of this return value here:
    // https://stackoverflow.com/questions/43116/how-can-i-run-an-external-program-from-c-and-parse-its-output/28971647#comment60311936_28971647
    int retval2 = pclose(fp);
    if (retval2 == -1)
    {
        printf("\nError obtaining the cmd's exit status code.\n");
        return -3;
    }
    else if (retval2 != 0)
    {
        printf("\nCommand exited with exit status code %i.\n", retval2);
        return -4;
    }

    char keyPressed = buf[0];
    return keyPressed;
}

// int main(int argc, char *argv[])  // alternative prototype
int main()
{
    printf("Press any key to continue: ");
    fflush(stdout);
    char keyPressed = getKeypress();
    if (keyPressed > 0)
    {
        printf("\nKey pressed = %c\n", keyPressed);
    }

    return 0;
}

Команды компиляции и запуска C и C++ являются частью вывода ниже. Вот несколько демонстраций:

В С:

      eRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=gnu17 read_keypress_system_call.c -o bin/a && bin/a
Press any key to continue:
Key pressed = P

ИЛИ, в С++:

      eRCaGuy_hello_world/c$ g++ -Wall -Wextra -Werror -O3 -std=c++17 read_keypress_system_call.c -o bin/a && bin/a
Press any key to continue:
Key pressed = u

Смотрите также:

  1. Чтобы сделать еще один шаг вперед, см. мой другой ответ здесь, где я обнаруживаю и анализирую 3 символа за раз, чтобы обнаруживать нажатия клавиш со стрелками Up, Down, Leftили Right: Нажатия клавиш чтения в C ex. Клавиши со стрелками, клавиша ввода

Использованная литература:

  1. Как я научился читать из канала, чтобы получить вывод системного вызова:
    1. Как я могу запустить внешнюю программу из C и проанализировать ее вывод?
    2. Есть ли способ получить вывод команды linux (например, ifconfig) в файле .txt с помощью программы C? [дубликат]
    3. Как составить и использовать popen()в C: использовать -std=gnu17вместо -std=c17: popen объявлен неявно, хотя добавлен #include &amp;lt;stdio.h&amp;gt;
  2. [мой ответ] Как читать без блокировки через bash: сценарий оболочки отвечает на нажатие клавишискрипт оболочки отвечает на нажатие клавиши
  3. [мой ответ] Как мне прочитать клавишу Enter в качестве ввода в C?

Все 3 вопроса и ответа по этой теме

  1. Захват символов из стандартного ввода, не дожидаясь нажатия клавиши ввода
  2. C неблокирующий ввод с клавиатуры
  3. Как избежать нажатия Enter с помощью getchar() для чтения только одного символа?

Вот версия, которая не распространяется на систему (написана и протестирована на 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 );
}

Вариант ответа ssinfod для Linux, на мой вкус немного чище, реализованный для wcout а также wchar_t, и стирает недопустимые символы без ошибок.

      #include <functional>

//For Linux kbhit(). For Windows, use conio.h.
#ifdef __unix__
  #include <sys/ioctl.h> //For FIONREAD.
  #include <termios.h>

  //Call this at program start to setup for kbhit.
  void initTerminalInput()
  {
    //Disable internal buffering.
    std::wcout << std::unitbuf;

    //Turn off line buffering.
    struct termios term;
    tcgetattr(0, &term);
    term.c_lflag &= ~ICANON;
    tcsetattr(0, TCSANOW, &term);
    setbuf(stdin, NULL);
  }

  //Returns 0 if there's no input character to read.
  int kbhit()
  {
    static int nbbytes;
    ioctl(0, FIONREAD, &nbbytes);
    return nbbytes;
  }
#endif

//Waits for and retrieves a single validated character, calling a validation function on each character entered and
//erasing any that are invalid (when the validation function returns false).
static wchar_t getWChar(std::function<bool(wchar_t)> validationFunction)
{
  static wchar_t inputWChar;
  do
  {
    //Wait until there's an input character.
    while (!kbhit())
    {
    }
    inputWChar = getwchar();
    //Validate the input character.
    if (validationFunction(inputWChar))
    {
      //Valid.
      break;
    }
    else
    {
      //Erase the invalid character.
      std::wcout << L"\b \b";
    }
  } while (true);
  return inputWChar;
}

В приведенном ниже примере я хотел, чтобы пользователь ввел 1, 2 или 3. Любые другие введенные символы не будут отображаться, и он будет ждать, пока не будет нажат один из допустимых символов:

      int main()
{
  #ifdef __unix__
    initTerminalInput();
  #endif

  getWChar([] (wchar_t inputWChar)
  {
    return (inputWChar >= L'1' && inputWChar <= L'3');
  });

  return 0;
}

В WINDOWS делаю так:

      #include <Windows.h>
int main()
{
    system("PAUSE>NUL");
    if (GetKeyState(0x41/*(the 'A' key, choosen e.g.)*/) & (0xff80/*That stands for "Default state / Key Down"*/)) {
        //whatever I want to do after 'A' is pressed
    }
}

Здесь можно найти список ключей со своим шестнадцатеричным значением для каждого:https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes

Другие вопросы по тегам