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.

124voto

Johannes Schaub - litb Points 256113

Ce n'est pas possible de manière portable en C++ pur, car cela dépend trop du terminal utilisé qui peut être connecté à stdin (ils sont généralement en mode tampon ligne par ligne). Vous pouvez cependant utiliser une bibliothèque pour cela :

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

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

Je vous recommanderais d'utiliser curses si vous visez la compatibilité multiplateforme. Cela dit, je suis sûr qu'il existe des fonctions que vous pouvez utiliser pour désactiver le tamponnage en ligne (je crois que cela s'appelle le mode "raw", par opposition au mode "cooked" - consultez man stty). Curses gérerait cela pour vous de manière portable, si je ne me trompe pas.

15 votes

Notez qu'aujourd'hui, ncurses est la variante recommandée de curses.

13 votes

Si vous avez besoin d'une bibliothèque, comment l'ont-ils créée? Pour créer la bibliothèque, vous avez certainement dû la programmer et cela signifie que n'importe qui pourrait la programmer, donc pourquoi ne pourrions-nous pas tout simplement programmer le code de la bibliothèque dont nous avons besoin nous-mêmes? Cela rendrait également faux le "Cela n'est pas possible de manière portable en c++ pur"

2 votes

@kid8 je ne comprends pas

99voto

Sur Linux (et d'autres systèmes de type unix) cela peut être fait de la manière suivante :

#include 
#include 

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

Essentiellement, vous devez désactiver le mode canonique (et le mode écho pour supprimer l'écho).

0 votes

J'ai essayé de mettre en œuvre ce code, mais j'ai obtenu une erreur lors de l'appel à read. J'ai inclus les deux en-têtes.

7 votes

Peut-être que ce code est incorrect, car read() est un appel système POSIX défini dans unistd.h. stdio.h pourrait l'inclure par coïncidence, mais vous n'avez en fait pas besoin de stdio.h pour ce code du tout ; remplacez-le par unistd.h et il devrait fonctionner correctement.

1 votes

Je ne sais pas comment je me suis retrouvé ici alors que je cherchais comment obtenir une entrée clavier dans le terminal ROS sur Raspberry Pi. Ce morceau de code fonctionne pour moi.

28voto

cwhiii Points 365

J'ai trouvé ceci sur un autre forum en cherchant à résoudre le même problème. Je l'ai légèrement modifié par rapport à ce que j'ai trouvé. Ça marche très bien. Je suis sous OS X, donc si vous utilisez Microsoft, vous devrez trouver la commande system() correcte pour passer en modes brut et normal.

#include  
#include   
using namespace std;  

int main() { 
  // Afficher le message 
  cout << "Appuyez sur n'importe quelle touche pour continuer..." << endl; 

  // Mettre le terminal en mode brut 
  system("stty raw"); 

  // Attendre la saisie d'un caractère 
  char input = getchar(); 

  // Afficher la saisie :
  cout << "--" << input << "--";

  // Réinitialiser le terminal en mode normal "cooked" 
  system("stty cooked"); 

  // Et voilà 
  return 0; 
}

17 votes

Bien que cela fonctionne, pour ce que ça vaut, lancer une commande système n'est généralement pas la "meilleure" façon de le faire à mon avis. Le programme stty est écrit en C, donc vous pouvez inclure ou et appeler le même code que stty utilise, sans dépendre d'un programme externe/fork/quoi que ce soit.

1 votes

J'avais besoin de ça pour des trucs aléatoires de concept et pour m'amuser. C'était exactement ce dont j'avais besoin. Merci. Il convient de noter : je mettrais définitivement le stty cooked à la fin du programme sinon votre shell restera en mode stty raw, ce qui a essentiellement cassé mon shell lol après l'arrêt du programme.

0 votes

Je pense que vous avez oublié #include

14voto

hasenj Points 36139

Si vous êtes sur Windows, vous pouvez utiliser PeekConsoleInput pour détecter s'il y a une entrée,

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

puis utilisez ReadConsoleInput pour "consommer" le caractère d'entrée ..

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, c'est issu de vieux code que j'ai, donc vous devez un peu le bidouiller.

La bonne nouvelle, cependant, est que cela lit l'entrée sans demander quoi que ce soit, donc les caractères ne sont pas du tout affichés.

13voto

CONIO.H

les fonctions dont vous avez besoin sont:

int getch();
Prototype
    int _getch(void); 
Description
    _getch obtient un caractère à partir de stdin. L'entrée n'est pas mise en mémoire tampon, et cette routine 
    retournera dès qu'un caractère est disponible sans attendre un retour à la ligne. Le caractère n'est pas affiché sur stdout.
    _getch contourne la mise en mémoire tampon normale réalisée par getchar et getc. ungetc 
    ne peut pas être utilisé avec _getch. 
Synonyme
    Fonction: getch 

int kbhit();
Description
    Vérifie si une touche du clavier a été pressée mais pas encore lue. 
Valeur de retour
    Retourne une valeur non nulle si une touche a été pressée. Sinon, retourne 0.

libconio http://sourceforge.net/projects/libconio

ou

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

3 votes

conio.h est complètement dépassé et n'est disponible que pour les anciennes APIs DOS, veuillez ne pas l'utiliser

0 votes

Cela fonctionne sur Windows (Visual Studio 2022). L'ancien conio.h DOS avait getch(); l'actuel a _getch(), tout comme la documentation ci-dessus. Cela fonctionne très bien dans les consoles Windows.

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