Comment simuler "Appuyez sur n'importe quelle touche pour continuer?"

87

J'essaie d'écrire un programme C ++ dans lequel, lorsque l'utilisateur entre un caractère à partir du clavier et qu'il doit passer à la ligne de code suivante.

Voici mon code:

char c;

cin>>c;

cout<<"Something"<<endl;

mais cela ne fonctionne pas, car il ne passe à la ligne suivante que lorsque j'entre un caractère et que j'appuie sur ENTRÉE.

OU

Si j'utilise ceci

cin.get() or cin.get(c)

il passe à la ligne d'instruction suivante lorsque j'appuie sur Entrée.

Mais je voulais qu'il passe à la ligne suivante sur n'importe quelle touche du clavier, comment cela peut-il être fait?

itsaboutcode
la source
Pour autant que je sache, le problème est que votre shell attend que vous appuyiez sur ENTRÉE ou EOF, puis laissera votre programme s'occuper de tout ce qui se trouve dans la mémoire tampon, ou quelque chose comme ça. Peut-être que quelqu'un avec plus de connaissances pourrait fournir une véritable explication. Mais je pense que ce n'est pas aussi facile qu'il y paraît.
Lucas le
7
De loin, le moyen le plus simple de gérer cela, et le seul moyen portable, est de changer votre invite de «Appuyez sur n'importe quelle touche pour continuer» à «Appuyez sur la touche Entrée pour continuer».
rob mayoff
@Lucas - J'utilise mac avec xcode.
itsaboutcode
@Lucas: Ce n'est pas le shell, c'est le programme lui-même.
Keith Thompson
@KeithThompson: C'était il y a plus de deux ans, mais je pense que j'essayais de faire valoir que la file d'attente d'entrée n'est pas gérée par le processus utilisateur mais à l'intérieur du noyau.
Lucas

Réponses:

64

Sous Windows:

system("pause");

et sur Mac et Linux:

system("read");

affichera "Appuyez sur n'importe quelle touche pour continuer ..." et, évidemment, attendez qu'une touche soit enfoncée. J'espère que c'est ce que tu voulais dire

MrMartin
la source
5
systemappelle un programme externe. Vous n'avez pas besoin d'appeler un programme externe pour attendre une touche! C'est comme appeler une autre personne pour allumer votre ordinateur lorsque vous êtes assis devant lui - c'est une solution lente.
GingerPlusPlus
8
C'est vrai, mais ce n'est qu'une ligne! Parfois, cela ne vaut tout simplement pas la peine de sauver l'ordinateur quelques cycles ...
Elliot Cameron
14
Oubliez la lenteur, il appelle le premier programme nommé "pause" qu'il trouve dans PATH, et lui donne le niveau de privilège du programme appelant. Même si vous êtes un étudiant qui teste simplement votre propre code, il s'agit toujours d'un risque de sécurité énorme.
Anne Quinn
6
@XDfaceme - pause est un programme réel que system () demande à Windows de s'exécuter. Si toutefois, il existe un autre programme appelé «pause» et que Windows trouve ce programme en premier, il l'exécutera à la place. Je suppose que j'ai peut-être un peu exagéré la signification, mais il est toujours plus facile d'utiliser simplement cin.get () et d'appuyer sur retour pour mettre en pause / reprendre un programme
Anne Quinn
2
Je ne suis pas fan des absolus. Faire une déclaration générale que le système ("pause") est un risque pour la sécurité et qu'il est mauvais même lorsque vous testez votre propre code, c'est exagérer les choses ici. cin.get () est peut-être la bonne solution, mais elle ne résout pas la question posée ... cin.get () ne répond qu'après avoir appuyé sur "enter" ou "return". La question était précisément de répondre à toute pression sur une touche. système ("pause") fait exactement cela. Tout d'abord, le seul endroit où j'ai vu cela comme utile est dans une classe de développement lors du débogage à partir de Visual Studio, donc je pense que le système ("pause") fonctionne
Gregor
52

Si vous êtes sous Windows, vous pouvez utiliser kbhit()ce qui fait partie de la bibliothèque d'exécution Microsoft. Si vous êtes sous Linux, vous pouvez implémenter kbhitainsi ( source ):

#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>

int kbhit(void)
{
  struct termios oldt, newt;
  int ch;
  int oldf;

  tcgetattr(STDIN_FILENO, &oldt);
  newt = oldt;
  newt.c_lflag &= ~(ICANON | ECHO);
  tcsetattr(STDIN_FILENO, TCSANOW, &newt);
  oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
  fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);

  ch = getchar();

  tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
  fcntl(STDIN_FILENO, F_SETFL, oldf);

  if(ch != EOF)
  {
    ungetc(ch, stdin);
    return 1;
  }

  return 0;
}

Mise à jour: La fonction ci-dessus fonctionne sur OS X (au moins, sur OS X 10.5.8 - Leopard, donc je m'attendrais à ce qu'elle fonctionne sur les versions plus récentes d'OS X). Cet élément essentiel peut être enregistré kbhit.cet compilé sous Linux et OS X avec

gcc -o kbhit kbhit.c

Lorsqu'il est exécuté avec

./kbhit

Il vous invite à appuyer sur une touche et se termine lorsque vous appuyez sur une touche (non limité à Entrée ou aux touches imprimables).

@Johnsyweb - veuillez préciser ce que vous entendez par «réponse canonique détaillée» et «toutes les préoccupations». Aussi, re "multi-plateforme": Avec cette implémentation de, kbhit()vous pouvez avoir les mêmes fonctionnalités dans un programme C ++ sous Linux / Unix / OS X / Windows - à quelles autres plates-formes pourriez-vous faire référence?

Mise à jour supplémentaire pour @Johnsyweb: les applications C ++ ne vivent pas dans un environnement C ++ hermétiquement fermé. Une des principales raisons du succès de C ++ est l'interopérabilité avec C. Toutes les plates-formes traditionnelles sont implémentées avec des interfaces C (même si l'implémentation interne utilise C ++), donc votre discours sur "l'héritage" semble déplacé. De plus, comme nous parlons d'une seule fonction, pourquoi avez-vous besoin de C ++ pour cela ("C avec classes")? Comme je l'ai souligné, vous pouvez écrire en C ++ et accéder facilement à cette fonctionnalité, et il est peu probable que les utilisateurs de votre application se soucient de la façon dont vous l' avez implémentée.

Vinay Sajip
la source
C'est dommage qu'il n'y ait pas de manière standard de gérer la console, vous devez toujours utiliser de la magie du système d'exploitation
Vargas
1
@Johnsyweb: Merci vous , mais votre exemple montre des caractéristiques qui me dépriment à propos de C ++. Votre kbhit()fonction déclare une terminal_settingsvariable dans une ligne qui semble ne rien faire, et pourtant elle fait tout. Certains pourraient penser que c'est bien, mais je trouve que c'est obscur au point d'être illisible.
Vinay Sajip
1
@VinaySajip: Mon implémentation utilise RAII , qui est l'un des idiomes les plus importants en C ++. Bien que ce ne soit pas strictement nécessaire dans cet exemple, j'ai trouvé que c'était une bonne habitude à prendre lorsque vous souhaitez enregistrer un état et le restaurer.
Johnsyweb
4
@Johnsyweb: Je suis conscient de RAII et des avantages qu'il apporte, je suis moi-même un ancien C ++. Mon argument demeure: il n'y a pas de lien clair entre la déclaration et ce qu'elle fait dans les coulisses. Bien qu'il puisse utiliser des idiomes C ++ brillants plutôt que du vieux C, je pense qu'il fait peu pour éclairer et beaucoup pour obscurcir ce qui se passe. Cela ne vous concerne en aucun cas personnellement - c'est juste une opinion sur la façon dont le C ++ peut être utilisé pour créer du code inutilement difficile à comprendre. Je vais maintenant sortir de ma boîte à savon, dit Nuff.
Vinay Sajip le
2
C'est beau et tout, mais attrape toujours les ordures précédentes sur le stdin :-)
Tiago
8

Il n'y a pas de solution totalement portable.

La question 19.1 de la FAQ comp.lang.c couvre cela en profondeur, avec des solutions pour Windows, les systèmes de type Unix, et même MS-DOS et VMS.

Un résumé rapide et incomplet:

  • Vous pouvez utiliser la cursesbibliothèque; appel cbreak()suivi de getch()(à ne pas confondre avec la getch()fonction spécifique à Windows ). Notez que cursesprend généralement le contrôle du terminal, il est donc probable que ce soit excessif.
  • Vous pourrez peut-être utiliser ioctl()pour manipuler les paramètres du terminal.
  • Sur les systèmes compatibles POSIX, tcgetattr()et tcsetattr()peut être une meilleure solution.
  • Sous Unix, vous pouvez utiliser system()pour appeler la sttycommande.
  • Sous MS-DOS, vous pouvez utiliser getch()ou getche().
  • Sur VMS (maintenant appelé OpenVMS), les SMG$routines de gestion d'écran ( ) peuvent faire l'affaire.

Toutes ces solutions C devraient fonctionner aussi bien en C ++; Je ne connais aucune solution spécifique à C ++.

Keith Thompson
la source
Toutes ces solutions multi-plateformes pourraient faire appel à quelqu'un qui écrit une fonction qui est implémentée pour faire la bonne chose en fonction de votre plate-forme avec de nombreux pré-processeurs pour déterminer ce qu'est cette plate-forme.
CashCow
6

Pour obtenir cette fonctionnalité, vous pouvez utiliser la bibliothèque ncurses qui a été implémentée à la fois sur Windows et Linux (et MacOS pour autant que je sache).

Kirill V. Lyadvinsky
la source
Eh bien, c'est une sorte d'affectation dans laquelle je ne peux pas utiliser de bibliothèques externes, etc., donc il suffit de rester dans "contexte de chapitre" lox.
itsaboutcode
2
Ensuite, la seule option consiste à implémenter les fonctionnalités nécessaires pour chaque système d'exploitation que vous prévoyez de prendre en charge.
Kirill V.Lyadvinsky
1
ncurses est plus ou moins un client VT200. Un peu exagéré pour simplement lire à partir du TTY.
greyfade
5

Sous Windows, ce programme court atteint l'objectif: getchmet la console en pause jusqu'à ce qu'une touche soit enfoncée ( https://www.geeksforgeeks.org/difference-getchar-getch-getc-getche/ )

#include<iostream.h>
#include<conio.h>

using namespace std;

void  check()
{
    char chk; int j;
    cout<<"\n\nPress any key to continue...";
    chk=getch();
    j=chk;
    for(int i=1;i<=256;i++)
      if(i==j) break;
    clrscr();
}

void main()
{
    clrscr();
    check();
    cout<<"\n\nIt works!";
    getch();
}

Il est à noter que getch ne fait pas partie de la bibliothèque standard.

BoldX
la source
1
Ce programme très simple. C'est très facile à comprendre
BoldX
1
Veuillez expliquer en quoi getch()est différent de cin.getou cin>>, peut-être un lien vers la documentation
user2622016
1
conio est sur Windows uniquement
Andrew Wolfe
4

J'ai regardé ce que vous essayez de réaliser, car je me souviens que je voulais faire la même chose. Inspiré par Vinay, j'ai écrit quelque chose qui fonctionne pour moi et je comprends en quelque sorte. Mais je ne suis pas un expert, alors soyez prudent.

Je ne sais pas comment Vinay sait que vous utilisez Mac OS X. Mais cela devrait fonctionner un peu comme ça avec la plupart des systèmes d'exploitation de type Unix. Opengroup.org est vraiment utile en tant que ressource

Assurez-vous de vider le tampon avant d'utiliser la fonction.

#include <stdio.h>
#include <termios.h>        //termios, TCSANOW, ECHO, ICANON
#include <unistd.h>     //STDIN_FILENO


void pressKey()
{
    //the struct termios stores all kinds of flags which can manipulate the I/O Interface
    //I have an old one to save the old settings and a new 
    static struct termios oldt, newt;
    printf("Press key to continue....\n");

    //tcgetattr gets the parameters of the current terminal
    //STDIN_FILENO will tell tcgetattr that it should write the settings
    // of stdin to oldt
    tcgetattr( STDIN_FILENO, &oldt);
    //now the settings will be copied 
    newt = oldt;

    //two of the c_lflag will be turned off
    //ECHO which is responsible for displaying the input of the user in the terminal
    //ICANON is the essential one! Normally this takes care that one line at a time will be processed
    //that means it will return if it sees a "\n" or an EOF or an EOL
    newt.c_lflag &= ~(ICANON | ECHO );      

    //Those new settings will be set to STDIN
    //TCSANOW tells tcsetattr to change attributes immediately. 
    tcsetattr( STDIN_FILENO, TCSANOW, &newt);

    //now the char wil be requested
    getchar();

    //the old settings will be written back to STDIN
    tcsetattr( STDIN_FILENO, TCSANOW, &oldt);

}


int main(void)
{
  pressKey();
  printf("END\n");
  return 0;
}

O_NONBLOCK semble également être un indicateur important, mais cela n'a rien changé pour moi.

J'apprécie que des personnes ayant des connaissances plus approfondies puissent commenter cela et donner des conseils.

Lucas
la source
O_NONBLOCK doit être défini car sans lui, getchar () (qui appelle read ()) bloquera l'attente d'une entrée. La solution kbhit () indique si vous appuyez sur une touche, sans attendre; vous pouvez l'utiliser dans une boucle d'attente ou à toute autre fin, c'est donc une solution plus générale. Une solution sans O_NONBLOCK attendra qu'une touche soit appuyée - OK pour cette question mais moins généralement utile.
Vinay Sajip
4

Vous pouvez utiliser la fonction _getch spécifique à Microsoft :

#include <iostream>
#include <conio.h>
// ...
// ...
// ...
cout << "Press any key to continue..." << endl;
_getch();
cout << "Something" << endl;
Salman A
la source
3

Cela fonctionne sur une plate-forme Windows: il utilise directement les registres du microprocesseur et peut être utilisé pour vérifier la pression sur une touche ou le bouton de la souris

    #include<stdio.h>
    #include<conio.h>
    #include<dos.h>
    void main()
    {
     clrscr();
     union REGS in,out;
     in.h.ah=0x00;
     printf("Press any key : ");

     int86(0x16,&in,&out);
     printf("Ascii : %d\n",out.h.al);
     char ch = out.h.al;
     printf("Charcter Pressed : %c",&ch);
     printf("Scan code : %d",out.h.ah);
     getch();
    }
Rohit Vipin Mathews
la source
3

Si vous utilisez Visual Studio 2012 ou une version antérieure, utilisez la getch()fonction, si vous utilisez Visual Studio 2013 ou une version plus récente, utilisez _getch(). Vous devrez utiliser #include <conio.h>. Exemple:

#include <iostream>
#include <conio.h>

int main()
{
   std::cout << "Press any key to continue. . .\n";
   _getch(); //Or getch()
}
John Doe
la source
1

Vous pouvez utiliser la routine getchar .

À partir du lien ci-dessus:

/* getchar example : typewriter */
#include <stdio.h>

int main ()
{
  char c;
  puts ("Enter text. Include a dot ('.') in a sentence to exit:");
  do {
    c=getchar();
    putchar (c);
  } while (c != '.');
  return 0;
}
Nick Dandoulakis
la source
3
Je ne pense pas que ce soit ce que le PO demandait, si je le comprends bien. Vous devez toujours appuyer sur ENTRÉE pour remplir le tampon avant que getchar () puisse en lire.
Lucas
0

Vous pouvez également utiliser getch () depuis conio.h. Juste comme ça:

...includes, defines etc
void main()
{
//operator
getch(); //now this function is waiting for any key press. When you have pressed its     just     //finish and next line of code will be called
}

Donc, comme UNIX n'a ​​pas conio.h, nous pouvons simuler getch () par ce code (mais ce code déjà écrit par Vinary, mon échec):

#include <stdio.h>
#include <termios.h>
#include <unistd.h>

int mygetch( ) {
  struct termios oldt,
             newt;
  int            ch;
  tcgetattr( STDIN_FILENO, &oldt );
  newt = oldt;
  newt.c_lflag &= ~( ICANON | ECHO );
  tcsetattr( STDIN_FILENO, TCSANOW, &newt );
  ch = getchar();
  tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
  return ch;
}
m9_psy
la source
-1

Si vous recherchez la fonction kbhit () sur MSDN maintenant, cela indique que la fonction est obsolète. Utilisez plutôt _kbhit ().

#include <conio.h>
int main()
{
    _kbhit();
    return 0;
}
BG4WCE
la source
-1
#include <iostream>
using namespace std;

int main () {
    bool boolean;
    boolean = true;

    if (boolean == true) {

        cout << "press any key to continue";
        cin >> boolean;

    }
    return 0;
}
Jeffery
la source
-4

Utilisez simplement la system("pause");commande.

Toutes les autres réponses compliquent le problème.

Tchad
la source