Come simulare "Premere un tasto qualsiasi per continuare?"


87

Sto cercando di scrivere un programma C ++ in cui quando l'utente immette un carattere dalla tastiera e dovrebbe passare alla riga di codice successiva.

Ecco il mio codice:

char c;

cin>>c;

cout<<"Something"<<endl;

ma questo non funziona, perché passa alla riga successiva solo quando inserisco un carattere e poi premo INVIO.

O

Se uso questo

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

si sposta alla riga successiva di istruzioni quando si preme Invio.

Ma volevo che passasse alla riga successiva su qualsiasi tasto premuto sulla tastiera, come si può fare?


Per quanto ne so, il problema è che la tua shell sta aspettando che tu prema ENTER o EOF e poi lascerà che il tuo programma si occupi di tutto ciò che è nel buffer, o qualcosa del genere. Forse qualcuno con qualche conoscenza in più potrebbe fornire una vera spiegazione. Ma penso che non sia così facile come sembra.
Lucas

7
Il modo di gran lunga più semplice per gestire questo, e l'unico modo portatile, è modificare il prompt da "Premere un tasto qualsiasi per continuare" a "Premere il tasto Invio per continuare".
Rob Mayoff

@ Lucas - Sto usando Mac con xcode.
codice

@ Lucas: non è la shell, è il programma stesso.
Keith Thompson

@KeithThompson: Questo è più di due anni fa, ma penso che stavo cercando di sottolineare che la coda di input non viene gestita dal processo utente ma all'interno del kernel.
Lucas

Risposte:


65

Su Windows:

system("pause");

e su Mac e Linux:

system("read");

produrrà "Premere un tasto qualsiasi per continuare ..." e ovviamente, attendere che venga premuto un tasto qualsiasi. Spero che questo sia quello che intendevi


5
systemchiama il programma esterno. Non devi chiamare un programma esterno per aspettare qualsiasi chiave! È come chiamare un'altra persona per accendere il tuo computer quando sei seduto di fronte: questa è una soluzione lenta.
GingerPlusPlus

8
Vero, ma è lunga solo una riga! A volte non vale la pena salvare alcuni cicli del computer ...
Elliot Cameron

14
Dimentica la lentezza, chiama il primo programma chiamato "pause" che trova in PATH e gli assegna il livello di privilegio del programma chiamante. Anche se sei uno studente che sta solo testando il tuo codice, è comunque un enorme rischio per la sicurezza.
Anne Quinn

6
@XDfaceme - pause è un programma effettivo che system () chiede a Windows di eseguire. Se, tuttavia, c'è un altro programma chiamato "pause" e Windows lo trova prima, lo eseguirà. Suppongo di aver esagerato un po 'il significato, ma è ancora più facile usare cin.get () e premere Invio per mettere in pausa / riattivare un programma
Anne Quinn

2
Non sono un fan degli assoluti. Fare una dichiarazione generale che il sistema ("pausa") è un rischio per la sicurezza e che è dannoso anche quando si prova il proprio codice, qui le cose sono sproporzionate. cin.get () potrebbe essere la soluzione giusta, ma non risolve la domanda che veniva posta ... cin.get () risponde solo dopo aver premuto "invio" o "invio". La domanda specifica era rispondere a qualsiasi pressione di un tasto. system ("pause") fa esattamente questo. Prima di tutto, l'unico posto in cui l'ho visto come utile è in una classe dev durante il debug da Visual Studio, quindi penso che il sistema ("pausa") funzioni
Gregor

53

Se sei su Windows, puoi utilizzare kbhit()che fa parte della libreria di runtime di Microsoft. Se sei su Linux, puoi implementare kbhitcosì ( fonte ):

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

Aggiornamento: la funzione di cui sopra funziona su OS X (almeno su OS X 10.5.8 - Leopard, quindi mi aspetto che funzioni su versioni più recenti di OS X). Questa sintesi può essere salvata come kbhit.ce compilata sia su Linux che su OS X con

gcc -o kbhit kbhit.c

Quando corri con

./kbhit

Ti richiede di premere un tasto ed esce quando premi un tasto (non limitato a Invio o tasti stampabili).

@Johnsyweb - per favore elabora cosa intendi per "risposta canonica dettagliata" e "tutte le preoccupazioni". Inoltre, ri "multipiattaforma": con questa implementazione kbhit()puoi avere la stessa funzionalità in un programma C ++ su Linux / Unix / OS X / Windows - a quali altre piattaforme potresti fare riferimento?

Ulteriore aggiornamento per @Johnsyweb: le applicazioni C ++ non vivono in un ambiente C ++ sigillato ermeticamente. Una delle ragioni principali del successo di C ++ è l'interoperabilità con C. Tutte le piattaforme mainstream sono implementate con interfacce C (anche se l'implementazione interna utilizza C ++), quindi il tuo parlare di "legacy" sembra fuori luogo. Inoltre, poiché stiamo parlando di una singola funzione, perché hai bisogno di C ++ per questo ("C con classi")? Come ho sottolineato, puoi scrivere in C ++ e accedere facilmente a questa funzionalità, ed è improbabile che gli utenti della tua applicazione si preoccupino di come l' hai implementata.


È un peccato che non ci sia un modo standard per gestire la console, devi sempre usare un po 'di magia del sistema operativo
Vargas,

1
@Johnsyweb: Grazie te , ma i tuoi esempio mostra caratteristiche che mi deprimono su C ++. La tua kbhit()funzione dichiara una terminal_settingsvariabile in una riga che sembra non fare nulla, eppure fa tutto. Alcuni potrebbero pensare che sia pulito, ma trovo che sia oscuro al punto da renderlo illeggibile.
Vinay Sajip

1
@VinaySajip: la mia implementazione usa RAII , che è uno degli idiomi più importanti in C ++. Sebbene non sia strettamente necessario in questo esempio, ho trovato una buona abitudine entrare quando vuoi salvare uno stato e ripristinarlo.
Johnsyweb

4
@Johnsyweb: sono consapevole della RAII e dei vantaggi che porta, anch'io sono un vecchio esperto di C ++. Resta il mio punto: non c'è una chiara connessione tra la dichiarazione e ciò che sta facendo dietro le quinte. Anche se potrebbe utilizzare idiomi C ++ brillanti piuttosto che il vecchio C, la mia opinione è che fa poco per illuminare e molto per oscurare ciò che sta succedendo. Questo non è diretto a te personalmente in alcun modo - è solo un'opinione su come il C ++ può essere usato per creare codice che è inutilmente difficile da capire. Adesso tolgo la mia scatola di sapone ', disse nuff.
Vinay Sajip

2
È bello e tutto, ma cattura ancora qualsiasi spazzatura precedente sullo stdin :-)
Tiago

9

Non esiste una soluzione completamente portatile.

La domanda 19.1 delle FAQ di comp.lang.c copre questo aspetto in modo approfondito, con soluzioni per Windows, sistemi simili a Unix e persino MS-DOS e VMS.

Un riepilogo rapido e incompleto:

  • Puoi usare la curseslibreria; chiamata cbreak()seguita da getch()( da non confondere con la getch()funzione specifica di Windows ). Nota che cursesgeneralmente prende il controllo del terminale, quindi è probabile che sia eccessivo.
  • Potresti essere in grado di utilizzare ioctl()per manipolare le impostazioni del terminale.
  • Su sistemi compatibili con POSIX tcgetattr()e tcsetattr()potrebbe essere una soluzione migliore.
  • Su Unix, puoi usare system()per invocare il sttycomando.
  • In MS-DOS, puoi usare getch()o getche().
  • Su VMS (ora chiamato OpenVMS), le SMG$routine Screen Management ( ) potrebbero fare il trucco.

Tutte queste soluzioni C dovrebbero funzionare altrettanto bene in C ++; Non conosco nessuna soluzione specifica per C ++.


Tutte queste soluzioni multipiattaforma potrebbero fare con qualcuno che scrive una funzione implementata per fare la cosa giusta a seconda della tua piattaforma con molti pre-processori per determinare quale sia quella piattaforma.
CashCow

6

Per ottenere questa funzionalità è possibile utilizzare la libreria ncurses che è stata implementata sia su Windows che su Linux (e su MacOS per quanto ne so).


Bene, questo è un compito in cui non posso usare nessuna libreria esterna ecc., Quindi devo solo rimanere nel "contesto del capitolo" lox.
itsaboutcode

2
Quindi l'unica opzione è implementare le funzionalità necessarie per ogni sistema operativo che si prevede di supportare.
Kirill V. Lyadvinsky

1
ncurses è più o meno un client VT200. Un po 'eccessivo per la semplice lettura dal TTY.
dissolvenza grigia

5

In Windows, questo breve programma raggiunge l'obiettivo: getchmette in pausa la console finché non viene premuto un tasto ( 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();
}

Va notato che getch non fa parte della libreria standard.


1
Questo programma molto semplice. È molto facile da capire
BoldX

1
Si prega di spiegare in che modo getch()è diverso cin.geto cin>>, forse, un collegamento alla documentazione
user2622016

1
conio è solo su Windows
Andrew Wolfe

4

Ho esaminato ciò che stai cercando di ottenere, perché ricordo che volevo fare la stessa cosa. Ispirato da Vinay ho scritto qualcosa che funziona per me e in un certo senso lo capisco. Ma non sono un esperto, quindi fai attenzione.

Non so come Vinay sappia che stai usando Mac OS X. Ma dovrebbe funzionare in questo modo con la maggior parte dei sistemi operativi unix. Davvero utile come risorsa è opengroup.org

Assicurati di svuotare il buffer prima di utilizzare la funzione.

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

Anche O_NONBLOCK sembra essere una bandiera importante, ma per me non ha cambiato nulla.

Apprezzo se le persone con una conoscenza più approfondita commentassero questo e dessero qualche consiglio.


O_NONBLOCK dovrebbe essere impostato perché senza di esso, getchar () (che chiama read ()) bloccherà l'attesa dell'input. La soluzione kbhit () dice se viene premuto un tasto, senza aspettare; puoi usarlo in un ciclo di attesa o per qualsiasi altro scopo, quindi è una soluzione più generale. Una soluzione senza O_NONBLOCK aspetterà fino a quando non viene premuto un tasto - OK per questa domanda ma generalmente meno utile.
Vinay Sajip,

4

È possibile utilizzare la funzione specifica di Microsoft _getch :

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

3

Funziona su una piattaforma Windows: utilizza direttamente i registri del microprocessore e può essere utilizzato per controllare la pressione di un tasto o il pulsante del mouse

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

3

Se stai usando Visual Studio 2012 o versioni precedenti, usa la getch()funzione, se stai usando Visual Studio 2013 o versioni successive, usa _getch(). Dovrai usare #include <conio.h>. Esempio:

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

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

1

Puoi usare getchar routine .

Dal collegamento sopra:

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

3
Non credo che questo fosse quello che chiedeva l'OP, se lo capisco correttamente. Devi ancora premere INVIO per riempire il buffer prima che getchar () possa leggere da esso.
Lucas

0

Inoltre puoi usare getch () da conio.h. Proprio come questo:

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

Quindi, poiché UNIX non ha conio.h, possiamo simulare getch () con questo codice (ma questo codice è già stato scritto da Vinary, errore mio):

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

0

Un'altra opzione è usare i thread con i puntatori a funzione :

#include <iostream>
#include <thread> // this_thread::sleep_for() and thread objects
#include <chrono> // chrono::milliseconds()

bool stopWaitingFlag = false;

void delayms(int millisecondsToSleep) 
{
  std::this_thread::sleep_for(std::chrono::milliseconds(millisecondsToSleep));
}

void WaitForKey() 
{
  while (stopWaitingFlag == false)
  {
    std::cout<<"Display from the thread function"<<std::endl;
    delayms(1000);
  }
}

 

int main()
{
  std::thread threadObj(&WaitForKey);

  char userInput = '\0';
  while (userInput != 'y')
  {
    std::cout << "\e[1;31mWaiting for a key, Enter 'y' for yes\e[0m" << std::endl;
    std::cin >> userInput;
    if (userInput == 'y') {
      stopWaitingFlag = true;
    }
  }
  if (threadObj.joinable())
    threadObj.join();

  std::cout<<"Exit of Main function"<<std::endl;
  return 0;
}

-1

Se ora cerchi la funzione kbhit () su MSDN, si dice che la funzione è deprecata. Utilizza invece _kbhit ().

#include <conio.h>
int main()
{
    _kbhit();
    return 0;
}

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

-4

Usa semplicemente il file system("pause"); comando.

Tutte le altre risposte complicano eccessivamente la questione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.