web-dev-qa-db-fra.com

Capturez les caractères de l'entrée standard sans attendre que vous appuyiez sur l'entrée

Je ne me souviendrai jamais de la façon dont je fais cela parce que cela arrive si rarement pour moi. Mais en C ou C++, quel est le meilleur moyen de lire un caractère à partir d’une entrée standard sans attendre une nouvelle ligne (appuyez sur Entrée).

Aussi, idéalement, cela ne ferait pas écho au caractère saisi à l'écran. Je veux juste capturer les frappes sans affecter l'écran de la console.

158
Adam

Cela n’est pas possible de manière portable en C++ pur, car cela dépend trop du terminal utilisé pouvant être connecté à stdin (ils sont généralement dotés d’une mémoire tampon de ligne). Vous pouvez cependant utiliser une bibliothèque pour cela:

  1. conio disponible avec les compilateurs Windows. Utilisez la fonction _getch() pour vous attribuer un caractère sans attendre la touche Entrée. Je ne suis pas un développeur Windows fréquent, mais j'ai vu mes camarades de classe simplement inclure _<conio.h>_ et l'utiliser. Voir conio.h sur Wikipedia. Il répertorie getch(), qui est déclaré obsolète dans Visual C++.

  2. curses disponibles pour Linux. Des implémentations de curses compatibles sont également disponibles pour Windows. Il a également une fonction getch(). (essayez _man getch_ pour afficher sa page de manuel). Voir Curses sur Wikipedia.

Je vous recommanderais d'utiliser des malédictions si vous visez une compatibilité entre plates-formes. Cela dit, je suis sûr qu'il existe des fonctions que vous pouvez utiliser pour désactiver la mise en mémoire tampon de la ligne (je crois que cela s'appelle le "mode brut", par opposition au "mode cuit" - regardez dans _man stty_). Les malédictions s'en chargeraient de manière portable, si je ne me trompe pas.

89

Sous Linux (et d’autres systèmes de type Unix), cela peut se faire de la manière suivante:

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

Fondamentalement, vous devez désactiver le mode canonique (et le mode écho pour supprimer les échos).

76
anon

J'ai trouvé cela sur un autre forum tout en cherchant à résoudre le même problème. Je l'ai un peu modifié par rapport à ce que j'ai trouvé. Cela fonctionne très bien. J'utilise OS X, donc si vous utilisez Microsoft, vous devez trouver la commande system () appropriée pour passer aux modes brut et cuit.

#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; 
}
16
cwhiii

CONIO.H

les fonctions dont vous avez besoin sont:

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

ou

Implémentation Linux c ++ de conio.h http://sourceforge.net/projects/linux-conioh

14
dddomodossola
#include <conio.h>

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

Ceci utilise kbhit() pour vérifier si le clavier est enfoncé et utilise getch() pour obtenir le caractère sur lequel vous appuyez.

9
Joseph Dykstra

Si vous êtes sur Windows, vous pouvez utiliser PeekConsoleInput pour détecter toute entrée,

HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
DWORD events;
INPUT_RECORD buffer;
PeekConsoleInput( handle, &buffer, 1, &events );

utilisez ensuite ReadConsoleInput pour "consommer" le caractère saisi.

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

pour être honnête, cela provient d'un ancien code que j'ai, alors vous devez le manipuler un peu.

Ce qui est bien, c’est qu’il lit les entrées sans rien demander, les caractères ne sont donc pas affichés.

8
hasen

En supposant que Windows, examinez la fonction ReadConsoleInput.

5
Tritium

C et C++ ont une vue très abstraite des E/S et il n’existe pas de méthode standard pour faire ce que vous voulez. Il existe des méthodes standard pour extraire les caractères du flux d'entrée standard, le cas échéant, et rien d'autre n'est défini par l'une ou l'autre langue. Toute réponse devra donc être spécifique à la plate-forme, peut-être en fonction non seulement du système d'exploitation, mais également du cadre logiciel.

Il existe des hypothèses raisonnables, mais il n’ya aucun moyen de répondre à votre question sans savoir quel est votre environnement cible.

5
David Thornley

La chose la plus proche de portable est d'utiliser la bibliothèque ncurses pour mettre le terminal en "mode cbreak". L'API est gigantesque. les routines que vous voudrez le plus sont

  • initscr et endwin
  • cbreak et nocbreak
  • getch

Bonne chance!

4
Norman Ramsey

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

Voir le code ci-dessous:

// 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
ssinfod

J'ai toujours voulu une boucle pour lire mon entrée sans appuyer sur la touche de retour. cela a fonctionné pour moi.

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

    }
3
Setu Gupta

Ce qui suit est une solution extraite de Programmation Expert C: Deep Secrets, qui est supposé fonctionner sur SVr4. Il utilise stty et 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");
}
3
PolyThinker

ncurses est un bon moyen de le faire! De plus, c’est mon tout premier message (je me souviens bien), alors tout commentaire est le bienvenu. J'apprécierai les utiles, mais tous sont les bienvenus!

pour compiler: 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;
}
2
AngryDane

travaille pour moi sur windows:

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

Vous pouvez le faire de manière portable en utilisant SDL (la bibliothèque Simple DirectMedia Library), bien que je suppose que son comportement ne vous plaise pas. Lorsque j'ai essayé, je devais faire en sorte que SDL crée une nouvelle fenêtre vidéo (même si je n'en avais pas besoin pour mon programme) et que cette fenêtre "saisisse" presque toutes les entrées au clavier et à la souris (ce qui était correct pour mon utilisation, mais pouvait ennuyeux ou inutilisable dans d’autres situations). Je suppose que c'est exagéré et qu'il n'en vaut la peine que si la portabilité totale est indispensable. Sinon, essayez l'une des solutions suggérées.

Soit dit en passant, cela vous permettra d’appuyer sur une touche et de relâcher les événements séparément, si vous y tenez.

1
Ruchira