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.

1voto

user152170 Points 11

Vous pouvez le faire de manière portable en utilisant SDL (la bibliothèque Simple DirectMedia), bien que je soupçonne que vous n'aimerez peut-être pas son comportement. Quand je l'ai essayé, j'ai dû demander à SDL de créer une nouvelle fenêtre vidéo (même si je n'en avais pas besoin pour mon programme) et faire en sorte que cette fenêtre "capture" presque toutes les entrées au clavier et à la souris (ce qui était acceptable pour mon utilisation mais pourrait être ennuyeux ou inutilisable dans d'autres situations). Je soupçonne que c'est excessif et que cela ne vaut pas la peine à moins que la portabilité complète ne soit indispensable - sinon essayez l'une des autres solutions suggérées.

À propos, cela vous donnera des événements séparés pour l'appui sur une touche et la libération de celle-ci, si cela vous intéresse.

1voto

Andrew Points 959

Variante de la réponse de ssinfod pour Linux qui me semble un peu plus propre à mes goûts, implémentée pour wcout et wchar_t, et efface les caractères invalides sans bugs.

#include 

//Pour kbhit() sur Linux. Pour Windows, utilisez conio.h.
#ifdef __unix__
  #include  //Pour FIONREAD.
  #include 

  //À appeler au démarrage du programme pour configurer kbhit.
  void initTerminalInput()
  {
    //Désactive le buffer interne.
    std::wcout << std::unitbuf;

    //Désactive le buffering en ligne.
    struct termios term;
    tcgetattr(0, &term);
    term.c_lflag &= ~ICANON;
    tcsetattr(0, TCSANOW, &term);
    setbuf(stdin, NULL);
  }

  //Retourne 0 s'il n'y a pas de caractère d'entrée à lire.
  int kbhit()
  {
    static int nbbytes;
    ioctl(0, FIONREAD, &nbbytes);
    return nbbytes;
  }
#endif

//Attend et récupère un seul caractère validé, appelant une fonction de validation sur chaque caractère saisi et
//effaçant tout caractère invalide (lorsque la fonction de validation renvoie false).
static wchar_t getWChar(std::function validationFunction)
{
  static wchar_t inputWChar;
  do
  {
    //Attendre qu'un caractère d'entrée soit saisi.
    while (!kbhit())
    {
    }
    inputWChar = getwchar();
    //Valider le caractère d'entrée.
    if (validationFunction(inputWChar))
    {
      //Valide.
      break;
    }
    else
    {
      //Effacer le caractère invalide.
      std::wcout << L"\b \b";
    }
  } while (true);
  return inputWChar;
}

Dans l'exemple ci-dessous, je voulais que l'utilisateur saisisse soit 1, 2, ou 3. Tous les autres caractères saisis ne seront pas affichés, et le programme attendra l'appui sur l'un des caractères valides:

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

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

  return 0;
}

0voto

Jeff Szuhay Points 142

Voici une version qui ne fait pas appel au système (écrite et testée sous macOS 10.14)

#include 
#include 
#include 
#include 

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( "entrez une chaîne de caractères: ");
  getStr( stringBuffer , maxChars );
  printf( "vous avez entré: [%s]\n" , stringBuffer );
}

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