224 votes

Capturer les caractères à partir de l'entrée standard sans attendre que la touche Entrée soit pressée

Je ne peux jamais me rappeler comment je fais cela car cela arrive si rarement pour moi. Mais en C ou C ++, quelle est la meilleure façon de lire un caractère de l'entrée standard sans attendre un retour à la ligne (appuyez sur entrée).

Idéalement, cela ne devrait pas non plus afficher le caractère d'entrée à l'écran. Je veux juste capturer les frappes sans affecter l'écran de la console.

1 votes

@adam - Pouvez-vous clarifier : Voulez-vous une fonction qui retournera immédiatement si aucun caractère n'est disponible, ou une qui attendra toujours une seule pression de touche?

3 votes

@Roddy - Je veux une fonction qui attendra toujours une seule frappe de touche.

9voto

Joseph Dykstra Points 93
#inclure 

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

Cela utilise kbhit() pour vérifier si le clavier est enfoncé et utilise getch() pour obtenir le caractère qui est enfoncé.

7 votes

conio.h? "conio.h est un fichier d'en-tête C utilisé dans d'anciens compilateurs MS-DOS pour créer des interfaces utilisateur textuelles." Semble quelque peu dépassé.

7voto

ssinfod Points 549

J'utilise kbhit() pour voir si un caractère est présent et ensuite getchar() pour lire les données. Sur Windows, vous pouvez utiliser "conio.h". Sur Linux, vous devrez implémenter votre propre fonction kbhit().

Voir le code ci-dessous:

// kbhit
#include 
#include  // Pour FIONREAD
#include 
#include 

int kbhit(void) {
    static bool initflag = false;
    static const int STDIN = 0;

    if (!initflag) {
        // Utiliser termios pour désactiver la mise en mémoire tampon en mode ligne
        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 est STDIN
    return nbbytes;
}

// main
#include 

int main(int argc, char** argv) {
    char c;
    //setbuf(stdout, NULL); // Optionnel: pas de mise en mémoire tampon.
    //setbuf(stdin, NULL);  // Optionnel: pas de mise en mémoire tampon.
    printf("Appuyez sur une touche");
    while (!kbhit()) {
        printf(".");
        fflush(stdout);
        sleep(1);
    }
    c = getchar();
    printf("\nCaractère reçu: %c\n", c);
    printf("Terminé.\n");

    return 0;
}

0 votes

A posté une variante de cette solution ici : stackoverflow.com/a/67363091/1599699 Ça marche bien, merci.

6voto

AngryDane Points 59

Ncurses offre un moyen agréable de le faire ! Aussi, ceci est mon tout premier article (dont je me souvienne), donc tous les commentaires sont les bienvenus. J'apprécierai des commentaires utiles, mais tous sont les bienvenus !

pour compiler : g++ -std=c++11 -pthread -lncurses .cpp -o

#include 
#include 
#include 

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)
    {
        // faire du travail
    }

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

char get_keyboard_input()
{
    char input = '0';
    while(input != 'q')
    {
        input = getch();
    }
    return input;
}

6voto

ProjectPhysX Points 1651

Comme les solutions précédentes ne fonctionnent pas sur toutes les plates-formes et ont des problèmes avec les touches spéciales, voici ma solution qui fonctionne à la fois sur Windows et Linux et utilise un minimum de bibliothèques externes ( Windows.h pour Windows et sys/ioctl.h + termios.h pour Linux).

Pour les caractères ASCII (nouvelle ligne/tabulation/espace/retour/suppression, !"#$%&'()*+,-./0-9:;<=>?@A-Z[]^_`a-z{|}~üä), les codes ASCII (nombres positifs) sont renvoyés et pour les touches spéciales (touches fléchées, page haut/bas, pos1/fin, échappement, insertion, F1-F12), le négatif de Codes de la clé virtuelle de Windows (nombres négatifs) sont renvoyés.

#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: ¹ (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; // 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   0: cotinu;
          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

Enfin, voici un exemple d'utilisation :

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

0 votes

C'est la réponse exacte que je cherchais! Merci! Je me demande juste, quelle est la finalité de la boucle: fflush(); ioctl(0, FIONREAD,...)?

0 votes

@Eugene K de rien! La boucle fait que key_press() attende qu'une touche soit pressée, en vérifiant périodiquement l'entrée de la clé.

1 votes

Compris ! Je pensais que getchar() allait bloquer (et attendre) une entrée clavier.

4voto

Tritium Points 61

En supposant que vous utilisez Windows, jetez un œil à la fonction ReadConsoleInput.

0 votes

1 votes

@JohnHenckel Cela concerne le langage C#, vous voulez probablement apprendre.microsoft.com/en-us/windows/console/readconsoleinput

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X