Ich versuche, ein Programm in C (unter Linux) zu schreiben, das eine Schleife durchläuft, bis der Benutzer eine Taste drückt, aber keinen Tastendruck erfordern sollte, um jede Schleife fortzusetzen.
Gibt es eine einfache Möglichkeit, dies zu tun? Ich denke, ich könnte es vielleicht tun select() aber das scheint eine Menge Arbeit zu sein.
Alternativ gibt es eine Möglichkeit, a zu fangen Strg–c Tastendruck, um eine Bereinigung durchzuführen, bevor das Programm geschlossen wird, anstatt io nicht zu blockieren?
wie wär’s mit Threaderöffnung?
– Nathan B
26. Mai 2020 um 14:10 Uhr
Ich empfehle einen separaten Thread, der die Tastatureingabe blockiert und die Eingabe bei jedem Tastendruck an den anderen Haupt-Thread weiterleitet. Hier ist ein Beispiel, das ich in Python geschrieben habe. Stellen Sie für C sicher, dass Sie Thread-sichere Message-Passing-Warteschlangen verwenden oder den Abruf von Mutexes nach Bedarf blockieren. Die Python-Warteschlangenbibliothek ist bereits Thread-sicher, aber eine solche Sicherheit für Warteschlangen gibt es natürlich weder in C noch in C++.
– Gabriel Staples
3. Februar um 21:28 Uhr
Siehe auch: Wie vermeide ich es, die Eingabetaste mit getchar() zu drücken, um nur ein einzelnes Zeichen zu lesen?
– Gabriel Staples
3. Februar um 22:54 Uhr
Alnitak
Wie bereits erwähnt, können Sie verwenden sigaction um Strg-C zu fangen, oder select um jede Standardeingabe abzufangen.
Beachten Sie jedoch, dass Sie bei der letzteren Methode auch das TTY so einstellen müssen, dass es sich im Zeichen-zu-Zeit-Modus befindet und nicht im Zeilen-zu-Zeit-Modus. Letzteres ist die Standardeinstellung – wenn Sie eine Textzeile eingeben, wird sie nicht an die Standardeingabe des laufenden Programms gesendet, bis Sie die Eingabetaste drücken.
Sie müssten die verwenden tcsetattr() Funktion zum Deaktivieren des ICANON-Modus und wahrscheinlich auch zum Deaktivieren von ECHO. Aus dem Speicher heraus müssen Sie das Terminal auch wieder in den ICANON-Modus versetzen, wenn das Programm beendet wird!
Nur der Vollständigkeit halber, hier ist ein Code, den ich gerade erstellt habe (Achtung: keine Fehlerprüfung!), der ein Unix-TTY einrichtet und das DOS emuliert <conio.h> Funktionen kbhit() und getch():
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <termios.h>
struct termios orig_termios;
void reset_terminal_mode()
{
tcsetattr(0, TCSANOW, &orig_termios);
}
void set_conio_terminal_mode()
{
struct termios new_termios;
/* take two copies - one for now, one for later */
tcgetattr(0, &orig_termios);
memcpy(&new_termios, &orig_termios, sizeof(new_termios));
/* register cleanup handler, and set the new terminal mode */
atexit(reset_terminal_mode);
cfmakeraw(&new_termios);
tcsetattr(0, TCSANOW, &new_termios);
}
int kbhit()
{
struct timeval tv = { 0L, 0L };
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
return select(1, &fds, NULL, NULL, &tv) > 0;
}
int getch()
{
int r;
unsigned char c;
if ((r = read(0, &c, sizeof(c))) < 0) {
return r;
} else {
return c;
}
}
int main(int argc, char *argv[])
{
set_conio_terminal_mode();
while (!kbhit()) {
/* do some work */
}
(void)getch(); /* consume the character */
}
Soll getch() zurückgeben, welche Taste gedrückt wurde, oder soll es einfach das Zeichen verbrauchen?
– Salepate
19. Juli 2012 um 11:56 Uhr
@Salepate soll das Zeichen zurückgeben. Es ist das (void) Bit, das diesen Charakter bekommt und dann wegwirft.
– Alnitak
19. Juli 2012 um 12:05 Uhr
Oh, ich verstehe, vielleicht mache ich es falsch, aber wenn ich getch() auf einem printf(“%c”); Es zeigt seltsame Zeichen auf dem Bildschirm.
– Salepate
19. Juli 2012 um 12:07 Uhr
@ Max888 nicht portabel, aber Sie könnten das Programm selbst beenden, wenn das nächste abgerufene Zeichen ^ c (0x03) ist.
– Alnitak
27. Juli 2021 um 22:05 Uhr
Danke für eine elegante Lösung … Ich habe einen Interrupt-Timer, der dies verursacht kbhit() ab und zu zurückkehren -1 == EINTR …Das heisst kbhit() kehrt zurück true wann es (wohl) zurückkehren sollte false …Ich habe dies behoben, indem ich die geändert habe return Aussage ein kbhit() zu return (select(1, &fds, NULL, NULL, &tv) > 0);
– Blauchip
21. Oktober 2021 um 19:41 Uhr
select() ist ein bisschen zu niedrig für die Bequemlichkeit. Ich schlage vor, Sie verwenden die ncurses Bibliothek, um das Terminal in den Cbreak-Modus und den Verzögerungsmodus zu versetzen, und rufen Sie dann auf getch()die zurückkehren wird ERR wenn kein Charakter bereit ist:
Ich habe auch über die Verwendung von Flüchen nachgedacht, aber das potenzielle Problem dabei ist, dass der Aufruf von initscr () den Bildschirm löscht und möglicherweise auch die Verwendung von normalem stdio für die Bildschirmausgabe behindert.
– Alnitak
16. Januar 2009 um 8:09 Uhr
Ja, Flüche sind ziemlich alles oder nichts.
– Norman Ramsey
17. Januar 2009 um 3:01 Uhr
Auf UNIX-Systemen können Sie verwenden sigaction Aufruf zum Registrieren eines Signal-Handlers für SIGINT Signal, das die Tastenfolge Strg+C darstellt. Der Signal-Handler kann ein Flag setzen, das in der Schleife überprüft wird, wodurch es entsprechend unterbrochen wird.
Ich denke, ich werde dies wahrscheinlich der Einfachheit halber tun, aber ich werde die andere Antwort akzeptieren, da sie näher an dem liegt, was der Titel verlangt.
– Zxaos
15. Januar 2009 um 23:37 Uhr
Jon Clegg
Du willst wahrscheinlich kbhit();
//Example will loop until a key is pressed
#include <conio.h>
#include <iostream>
using namespace std;
int main()
{
while(1)
{
if(kbhit())
{
break;
}
}
}
dies funktioniert möglicherweise nicht in allen Umgebungen. Ein tragbarer Weg wäre, einen Überwachungsthread zu erstellen und ein Flag zu setzen getch();
Justin B
Eine andere Möglichkeit, eine nicht blockierende Tastatureingabe zu erhalten, besteht darin, die Gerätedatei zu öffnen und sie zu lesen!
Sie müssen die Gerätedatei kennen, nach der Sie suchen, eine von /dev/input/event*. Sie können cat /proc/bus/input/devices ausführen, um das gewünschte Gerät zu finden.
Dieser Code funktioniert bei mir (als Administrator ausführen).
fantastisches Beispiel, aber ich bin auf ein Problem gestoßen, bei dem Xorg weiterhin Tastenereignisse empfangen hat, wenn das Ereignisgerät aufgehört hat, zu senden, wenn mehr als sechs Tasten gehalten wurden: \ seltsam, oder?
– ThorSummoner
26. Dezember 2019 um 7:57 Uhr
Frosch
Dazu kann die Curses-Bibliothek verwendet werden. Na sicher, select() und Signalhandler können bis zu einem gewissen Grad ebenfalls verwendet werden.
fantastisches Beispiel, aber ich bin auf ein Problem gestoßen, bei dem Xorg weiterhin Tastenereignisse empfangen hat, wenn das Ereignisgerät aufgehört hat, zu senden, wenn mehr als sechs Tasten gehalten wurden: \ seltsam, oder?
– ThorSummoner
26. Dezember 2019 um 7:57 Uhr
Einzeln
Wenn Sie glücklich sind, nur Control-C zu fangen, ist es eine beschlossene Sache. Wenn Sie wirklich nicht blockierende E/A, aber nicht die Curses-Bibliothek wollen, besteht eine andere Alternative darin, Lock, Stock und Barrel in die zu verschieben AT&T sfio Bibliothek. Es ist eine schöne Bibliothek, die auf C gemustert ist stdio aber flexibler, Thread-sicherer und performanter. (sfio steht für sichere, schnelle I/O.)
14203600cookie-checkC nicht blockierende Tastatureingabeyes
wie wär’s mit Threaderöffnung?
– Nathan B
26. Mai 2020 um 14:10 Uhr
Ich empfehle einen separaten Thread, der die Tastatureingabe blockiert und die Eingabe bei jedem Tastendruck an den anderen Haupt-Thread weiterleitet. Hier ist ein Beispiel, das ich in Python geschrieben habe. Stellen Sie für C sicher, dass Sie Thread-sichere Message-Passing-Warteschlangen verwenden oder den Abruf von Mutexes nach Bedarf blockieren. Die Python-Warteschlangenbibliothek ist bereits Thread-sicher, aber eine solche Sicherheit für Warteschlangen gibt es natürlich weder in C noch in C++.
– Gabriel Staples
3. Februar um 21:28 Uhr
Siehe auch: Wie vermeide ich es, die Eingabetaste mit getchar() zu drücken, um nur ein einzelnes Zeichen zu lesen?
– Gabriel Staples
3. Februar um 22:54 Uhr