Ich versuche, ein Programm in C (unter Linux) zu schreiben, das sich wiederholt, bis der Benutzer eine Taste drückt, aber keinen Tastendruck benötigt, um jede Schleife fortzusetzen.
Gibt es eine einfache Möglichkeit, dies zu tun? Ich denke, ich könnte es möglicherweise damit machen, select()
aber das scheint eine Menge Arbeit zu sein.
Gibt es alternativ eine Möglichkeit, einen ctrl- cTastendruck abzufangen, um eine Bereinigung durchzuführen, bevor das Programm geschlossen wird, anstatt io nicht zu blockieren?
c
linux
asynchronous
input
nonblocking
Zxaos
quelle
quelle
Antworten:
Wie bereits erwähnt, können Sie
sigaction
Strg-C oderselect
jede Standardeingabe abfangen .Beachten Sie jedoch, dass Sie bei der letzteren Methode auch die TTY so einstellen müssen, dass sie sich im Zeichen-zu-Zeit-Modus und nicht im Zeilen-zu-Zeit-Modus befindet. Letzteres ist die Standardeinstellung. Wenn Sie eine Textzeile eingeben, wird diese erst dann an den Standard des laufenden Programms gesendet, wenn Sie die Eingabetaste drücken.
Sie müssten die
tcsetattr()
Funktion verwenden, um den ICANON-Modus auszuschalten und wahrscheinlich auch ECHO zu deaktivieren. Aus dem Speicher müssen Sie das Terminal auch beim Beenden des Programms wieder in den ICANON-Modus versetzen!Der Vollständigkeit halber hier ein Code, den ich gerade aufgerissen habe (nb: keine Fehlerprüfung!), Der ein Unix-TTY einrichtet und die DOS-
<conio.h>
Funktionen emuliertkbhit()
undgetch()
:#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); } 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 */ }
quelle
(void)
Stück, das diesen Charakter bekommt und ihn dann wegwirft.select()
ist ein bisschen zu niedrig für die Bequemlichkeit. Ich schlage vor, dass Sie diencurses
Bibliothek verwenden, um das Terminal in den Break- und Delay-Modus zu versetzen, und dann aufrufengetch()
, der zurückkehrt,ERR
wenn kein Zeichen bereit ist:An diesem Punkt können Sie anrufen,
getch
ohne zu blockieren.quelle
Auf UNIX-Systemen können Sie mit
sigaction
call einen Signalhandler für ein Signal registrieren,SIGINT
das die Tastenfolge Control + C darstellt. Der Signalhandler kann ein Flag setzen, das in der Schleife überprüft wird, damit es entsprechend bricht.quelle
Sie wollen 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. Eine tragbare Möglichkeit wäre, einen Überwachungsthread zu erstellen und ein Flag zu setzen
getch();
quelle
Eine andere Möglichkeit, nicht blockierende Tastatureingaben zu erhalten, besteht darin, die Gerätedatei zu öffnen und zu lesen!
Sie müssen die gesuchte Gerätedatei kennen, eine von / dev / input / event *. Sie können cat / proc / bus / input / Geräte ausführen, um das gewünschte Gerät zu finden.
Dieser Code funktioniert für mich (als Administrator ausführen).
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <linux/input.h> int main(int argc, char** argv) { int fd, bytes; struct input_event data; const char *pDevice = "/dev/input/event2"; // Open Keyboard fd = open(pDevice, O_RDONLY | O_NONBLOCK); if(fd == -1) { printf("ERROR Opening %s\n", pDevice); return -1; } while(1) { // Read Keyboard Data bytes = read(fd, &data, sizeof(data)); if(bytes > 0) { printf("Keypress value=%x, type=%x, code=%x\n", data.value, data.type, data.code); } else { // Nothing read sleep(1); } } return 0; }
quelle
Zu diesem Zweck kann die Curses-Bibliothek verwendet werden. Natürlich
select()
können bis zu einem gewissen Grad auch Signalhandler verwendet werden.quelle
Wenn Sie glücklich sind, nur Control-C zu fangen, ist es ein abgeschlossenes Geschäft. Wenn Sie wirklich nicht blockierende E / A möchten, aber die Curses-Bibliothek nicht möchten, können Sie auch Sperre, Lagerbestand und Barrel in die AT & T-
sfio
Bibliothek verschieben . Es ist eine schöne Bibliothek mit C-Muster,stdio
aber flexibler, threadsicherer und leistungsfähiger. (sfio steht für sichere, schnelle E / A.)quelle
Es gibt keine tragbare Möglichkeit, dies zu tun, aber select () ist möglicherweise eine gute Möglichkeit. Weitere mögliche Lösungen finden Sie unter http://c-faq.com/osdep/readavail.html .
quelle
Sie können dies mit select wie folgt tun:
int nfds = 0; fd_set readfds; FD_ZERO(&readfds); FD_SET(0, &readfds); /* set the stdin in the set of file descriptors to be selected */ while(1) { /* Do what you want */ int count = select(nfds, &readfds, NULL, NULL, NULL); if (count > 0) { if (FD_ISSET(0, &readfds)) { /* If a character was pressed then we get it and exit */ getchar(); break; } } }
Nicht zu viel Arbeit: D.
quelle
nfds
sollte auf 1 gesetzt werden.Hier ist eine Funktion, die dies für Sie erledigt. Sie benötigen
termios.h
das, was mit POSIX-Systemen geliefert wird.#include <termios.h> void stdin_set(int cmd) { struct termios t; tcgetattr(1,&t); switch (cmd) { case 1: t.c_lflag &= ~ICANON; break; default: t.c_lflag |= ICANON; break; } tcsetattr(1,0,&t); }
tcgetattr
Aufschlüsselung : Ruft die aktuellen Terminalinformationen ab und speichert sie int
. Wenncmd
1 ist, wird das lokale Eingabeflag int
auf nicht blockierende Eingabe gesetzt. Andernfalls wird es zurückgesetzt. Ändert danntcsetattr
die Standardeingabe int
.Wenn Sie die Standardeingabe am Ende Ihres Programms nicht zurücksetzen, treten Probleme in Ihrer Shell auf.
quelle