Wie simuliere ich "Drücken Sie eine beliebige Taste, um fortzufahren?"

84

Ich versuche, ein C ++ - Programm zu schreiben, in dem der Benutzer, wenn er ein Zeichen über die Tastatur eingibt, in die nächste Codezeile wechseln soll.

Hier ist mein Code:

char c;

cin>>c;

cout<<"Something"<<endl;

Dies funktioniert jedoch nicht, da es nur in die nächste Zeile wechselt, wenn ich ein Zeichen eingebe und dann die EINGABETASTE drücke.

ODER

Wenn ich das benutze

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

Wenn ich die Eingabetaste drücke, wechselt es zur nächsten Anweisungszeile.

Aber ich wollte, dass es mit einer beliebigen Taste auf der Tastatur in die nächste Zeile wechselt. Wie kann das gemacht werden?

itsaboutcode
quelle
Soweit ich weiß, besteht das Problem darin, dass Ihre Shell darauf wartet, dass Sie ENTER oder EOF drücken, und Ihr Programm sich dann um alles kümmert, was sich im Puffer befindet, oder so etwas. Vielleicht könnte jemand mit etwas mehr Wissen eine echte Erklärung liefern. Aber ich denke, es ist nicht so einfach, wie es zuerst erscheint.
Lucas
7
Der bei weitem einfachste und tragbarste Weg, dies zu handhaben, besteht darin, die Eingabeaufforderung von "Drücken Sie eine beliebige Taste, um fortzufahren" in "Drücken Sie die Eingabetaste, um fortzufahren" zu ändern.
Rob Mayoff
@ Lucas - Ich benutze Mac mit Xcode.
ungefähr Code
@ Lucas: Es ist nicht die Shell, es ist das Programm selbst.
Keith Thompson
@KeithThompson: Dies ist mehr als zwei Jahre her, aber ich glaube, ich habe versucht, darauf hinzuweisen, dass die Eingabewarteschlange nicht vom Benutzerprozess, sondern im Kernel verarbeitet wird.
Lucas

Antworten:

62

Unter Windows:

system("pause");

und auf Mac und Linux:

system("read");

gibt "Drücken Sie eine beliebige Taste, um fortzufahren ..." aus und warten Sie, bis eine beliebige Taste gedrückt wird. Ich hoffe das hast du gemeint

MrMartin
quelle
5
systemruft externes Programm auf. Sie müssen kein externes Programm aufrufen, um auf einen Schlüssel zu warten! Es ist, als würde man eine andere Person anrufen, um den Computer einzuschalten, wenn Sie davor sitzen - dies ist eine langsame Lösung.
GingerPlusPlus
8
Stimmt, aber es ist nur eine Zeile lang! Manchmal lohnt es sich einfach nicht, den Computer einige Zyklen zu retten ...
Elliot Cameron
13
Vergessen Sie langsam, es ruft das erste Programm mit dem Namen "pause" auf, das es zufällig in PATH findet, und gibt ihm die Berechtigungsstufe des aufrufenden Programms. Selbst wenn Sie als Student nur Ihren eigenen Code testen, besteht immer noch ein massives Sicherheitsrisiko.
Anne Quinn
6
@XDfaceme - pause ist ein aktuelles Programm, das system () Windows zum Ausführen auffordert. Wenn es jedoch ein anderes Programm namens "pause" gibt und Windows dieses Programm zuerst findet, wird es stattdessen ausgeführt. Ich nehme an, ich hätte die Bedeutung ein wenig übertrieben, aber es ist immer noch einfacher, einfach cin.get () zu verwenden und die Eingabetaste zu drücken, um ein Programm anzuhalten / anzuhalten
Anne Quinn
2
Ich bin kein Fan von Absoluten. Die pauschale Aussage, dass das System ("Pause") ein Sicherheitsrisiko darstellt und dass es selbst dann schlecht ist, wenn Sie Ihren eigenen Code testen, bringt die Dinge hier in die Luft. cin.get () ist möglicherweise die richtige Lösung, löst jedoch nicht die gestellte Frage ... cin.get () antwortet erst, nachdem "Enter" oder "Return" gedrückt wurden. Die Frage war speziell, auf jeden Tastendruck zu antworten. System ("Pause") macht genau das. Zunächst einmal ist der einzige Ort, an dem ich dies als nützlich angesehen habe, eine Entwicklerklasse beim Debuggen in Visual Studio. Ich denke also, dass das System ("Pause") funktioniert
Gregor
51

Wenn Sie unter Windows arbeiten, können Sie das verwenden, kbhit()das Teil der Microsoft-Laufzeitbibliothek ist. Wenn Sie unter Linux arbeiten, können Sie Folgendes implementieren kbhit( Quelle ):

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

Update: Die obige Funktion funktioniert unter OS X (zumindest unter OS X 10.5.8 - Leopard, daher würde ich erwarten, dass sie unter neueren Versionen von OS X funktioniert). Dieser Kern kann unter kbhit.cLinux und OS X mit gespeichert und kompiliert werden

gcc -o kbhit kbhit.c

Wenn mit ausgeführt

./kbhit

Es fordert Sie zur Eingabe eines Tastendrucks auf und wird beendet, wenn Sie eine Taste drücken (nicht beschränkt auf Eingabetaste oder druckbare Tasten).

@Johnsyweb - bitte erläutern Sie, was Sie unter "detaillierter kanonischer Antwort" und "allen Bedenken" verstehen. Auch "plattformübergreifend": Mit dieser Implementierung kbhit()können Sie die gleiche Funktionalität in einem C ++ - Programm unter Linux / Unix / OS X / Windows haben - auf welche anderen Plattformen beziehen Sie sich möglicherweise?

Weiteres Update für @Johnsyweb: C ++ - Anwendungen leben nicht in einer hermetisch abgeschlossenen C ++ - Umgebung. Ein wichtiger Grund für den Erfolg von C ++ ist die Interoperabilität mit C. Alle Mainstream-Plattformen sind mit C-Schnittstellen implementiert (auch wenn die interne Implementierung C ++ verwendet), sodass Ihre Rede von "Legacy" fehl am Platz zu sein scheint. Und da wir über eine einzelne Funktion sprechen, warum benötigen Sie dafür C ++ ("C mit Klassen")? Wie bereits erwähnt, können Sie problemlos in C ++ schreiben und auf diese Funktionalität zugreifen, und es ist unwahrscheinlich, dass sich die Benutzer Ihrer Anwendung darum kümmern, wie Sie sie implementiert haben.

Vinay Sajip
quelle
Es ist eine Schande, dass es keine Standardmethode für den Umgang mit der Konsole gibt. Sie müssen immer etwas OS Magick verwenden
Vargas
1
@Johnsyweb: Viele Ihnen , aber Ihre Funktionen Beispiel zeigt , dass ich von C ++ drücken. Ihre kbhit()Funktion deklariert eine terminal_settingsVariable in einer Zeile, die scheinbar nichts tut, und dennoch alles tut. Einige mögen denken, dass es ordentlich ist, aber ich finde es bis zur Unlesbarkeit dunkel.
Vinay Sajip
1
@ VinaySajip: Meine Implementierung verwendet RAII , eine der wichtigsten Redewendungen in C ++. Obwohl dies in diesem Beispiel nicht unbedingt erforderlich ist, habe ich es als gute Angewohnheit empfunden, wenn Sie einen Status speichern und wiederherstellen möchten.
Johnsyweb
4
@ Johnsyweb: Ich bin mir RAII und der damit verbundenen Vorteile bewusst. Ich bin selbst eine alte C ++ - Hand. Mein Punkt bleibt: Es gibt keinen klaren Zusammenhang zwischen der Erklärung und dem, was sie hinter den Kulissen tut. Während es möglicherweise glänzende C ++ - Redewendungen anstelle von einfachem altem C verwendet, ist meine Ansicht, dass es wenig zum Beleuchten und viel zum Verschleiern der Vorgänge beiträgt. Dies richtet sich in keiner Weise an Sie persönlich - es ist nur eine Meinung darüber, wie C ++ zum Erstellen von Code verwendet werden kann, der unnötig schwer zu verstehen ist. Ich steige jetzt aus meiner Seifenkiste «, sagte Nuff.
Vinay Sajip
2
Es ist schön und alles, fängt aber immer noch jeden vorherigen Müll auf dem stdin :-)
Tiago
8

Es gibt keine vollständig tragbare Lösung.

Frage 19.1 der häufig gestellten Fragen zu comp.lang.c behandelt dies ausführlich mit Lösungen für Windows, Unix-ähnliche Systeme und sogar MS-DOS und VMS.

Eine schnelle und unvollständige Zusammenfassung:

  • Sie können die cursesBibliothek verwenden; Aufruf cbreak()gefolgt von getch()(nicht zu verwechseln mit der Windows-spezifischen getch()Funktion). Beachten Sie, dass im cursesAllgemeinen die Kontrolle über das Terminal übernommen wird, sodass dies wahrscheinlich zu viel des Guten ist.
  • Möglicherweise können Sie ioctl()die Terminaleinstellungen ändern.
  • Auf POSIX-kompatiblen Systemen tcgetattr()und tcsetattr()möglicherweise eine bessere Lösung.
  • Unter Unix können Sie system()den sttyBefehl aufrufen .
  • Unter MS-DOS können Sie getch()oder verwenden getche().
  • Unter VMS (jetzt OpenVMS genannt) können die SMG$Routinen von Screen Management ( ) den Trick ausführen.

Alle diese C-Lösungen sollten in C ++ gleich gut funktionieren. Ich kenne keine C ++ - spezifische Lösung.

Keith Thompson
quelle
All diese plattformübergreifenden Lösungen könnten mit jemandem zu tun haben, der eine Funktion schreibt, die implementiert ist, um abhängig von Ihrer Plattform mit vielen Vorprozessoren das Richtige zu tun, um festzustellen, um welche Plattform es sich handelt.
CashCow
6

Um diese Funktionalität zu erreichen, können Sie die ncurses- Bibliothek verwenden, die sowohl unter Windows als auch unter Linux (und meines Wissens unter MacOS) implementiert wurde.

Kirill V. Lyadvinsky
quelle
Nun, dies ist eine Art Aufgabe, in der ich keine externen Bibliotheken usw. verwenden kann, also muss ich einfach im "Kapitelkontext" lox bleiben.
ungefähr Code
2
Die einzige Möglichkeit besteht darin, die erforderlichen Funktionen für jedes Betriebssystem zu implementieren, das Sie unterstützen möchten.
Kirill V. Lyadvinsky
1
ncurses ist mehr oder weniger ein VT200-Client. Ein bisschen übertrieben, um einfach aus dem TTY zu lesen.
Greyfade
5

In Windows erreicht dieses kurze Programm das Ziel: getchDie Konsole wird angehalten, bis eine Taste gedrückt wird ( 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();
}

Es ist zu beachten, dass getch nicht Teil der Standardbibliothek ist.

BoldX
quelle
1
Dieses sehr einfache Programm. Es ist sehr leicht zu verstehen
BoldX
1
Bitte erklären Sie, wie getch()sich von cin.getoder cin>>, vielleicht ein Link zur Dokumentation unterscheidet
user2622016
1
conio ist nur auf Windows
Andrew Wolfe
4

Ich habe untersucht, was Sie erreichen wollen, weil ich mich erinnere, dass ich das Gleiche tun wollte. Inspiriert von Vinay habe ich etwas geschrieben, das für mich funktioniert und ich verstehe es irgendwie. Aber ich bin kein Experte, also seien Sie bitte vorsichtig.

Ich weiß nicht, woher Vinay weiß, dass Sie Mac OS X verwenden. Aber es sollte mit den meisten Unix-ähnlichen Betriebssystemen so funktionieren. Wirklich hilfreich als Ressource ist opengroup.org

Stellen Sie sicher, dass Sie den Puffer leeren, bevor Sie die Funktion verwenden.

#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 scheint auch eine wichtige Flagge zu sein, aber es hat nichts für mich geändert.

Ich schätze es, wenn Leute mit etwas tieferem Wissen dies kommentieren und Ratschläge geben würden.

Lucas
quelle
O_NONBLOCK sollte gesetzt werden, da getchar () (das read () aufruft) ohne es das Warten auf Eingabe blockiert. Die kbhit () -Lösung zeigt an, ob eine Taste gedrückt wird, ohne zu warten. Sie können es in einer Warteschleife oder für einen anderen Zweck verwenden, daher ist es eine allgemeinere Lösung. Eine Lösung ohne O_NONBLOCK wartet, bis eine Taste gedrückt wird - OK für diese Frage, aber weniger allgemein nützlich.
Vinay Sajip
4

Sie können die Microsoft-spezifische Funktion _getch verwenden :

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

Dies funktioniert auf einer Windows-Plattform: Sie verwendet die Mikroprozessorregister direkt und kann zum Überprüfen des Tastendrucks oder der Maustaste verwendet werden

    #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
quelle
2

Wenn Sie Visual Studio 2012 oder älter verwenden, verwenden Sie die getch()Funktion. Wenn Sie Visual Studio 2013 oder höher verwenden, verwenden Sie _getch(). Sie müssen verwenden #include <conio.h>. Beispiel:

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

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

Sie können die getchar- Routine verwenden.

Über den obigen Link:

/* 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
quelle
3
Ich glaube nicht, dass das OP darum gebeten hat, wenn ich ihn richtig verstehe. Sie müssen noch die EINGABETASTE drücken, um den Puffer zu füllen, bevor getchar () daraus lesen kann.
Lucas
0

Sie können auch getch () von conio.h verwenden. Genau wie dieser:

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

Da UNIX nicht über conio.h verfügt, können wir getch () mit diesem Code simulieren (aber dieser Code wurde bereits von Vinary geschrieben, mein Fehler):

#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
quelle
-1

Wenn Sie jetzt die Funktion kbhit () in MSDN nachschlagen, heißt es, dass die Funktion veraltet ist. Verwenden Sie stattdessen _kbhit ().

#include <conio.h>
int main()
{
    _kbhit();
    return 0;
}
BG4WCE
quelle
-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
quelle
-4

Verwenden Sie einfach den system("pause");Befehl.

Alle anderen Antworten erschweren das Problem.

Tschad
quelle