Я пытаюсь написать программу на C (в Linux), которая зацикливается, пока пользователь не нажмет клавишу, но не требует нажатия клавиши для продолжения каждого цикла.
Есть простой способ сделать это? Я полагаю, я мог бы сделать это сselect()
но это похоже на большую работу.
В качестве альтернативы, есть ли способ поймать ctrl- cнажатие клавиши для очистки перед закрытием программы вместо неблокирующего io?
c
linux
asynchronous
input
nonblocking
Zxaos
источник
источник
Ответы:
Как уже говорилось, вы можете использовать
sigaction
для перехвата ctrl-c илиselect
для перехвата любого стандартного ввода.Однако обратите внимание, что с помощью последнего метода вам также необходимо настроить TTY так, чтобы он был в режиме «посимвольно», а не построчно. Последний вариант используется по умолчанию - если вы вводите строку текста, она не отправляется на стандартный ввод запущенной программы, пока вы не нажмете клавишу ввода.
Вам нужно будет использовать
tcsetattr()
функцию, чтобы отключить режим ICANON, и, возможно, также отключить ECHO. По памяти вам также необходимо вернуть терминал в режим ICANON при выходе из программы!Просто для полноты, вот код, который я только что набрал (примечание: без проверки ошибок!), Который устанавливает Unix TTY и эмулирует
<conio.h>
функции DOSkbhit()
и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); } 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 */ }
источник
(void)
бит, который получает этот персонаж, а затем отбрасывает его.select()
слишком низкий уровень для удобства. Я предлагаю вам использоватьncurses
библиотеку, чтобы перевести терминал в режим cbreak и режим задержки, затем вызватьgetch()
, который вернется,ERR
если ни один символ не готов:В этот момент вы можете звонить
getch
без блокировки.источник
В системах UNIX вы можете использовать
sigaction
call для регистрации обработчикаSIGINT
сигнала для signal, который представляет собой комбинацию клавиш Control + C. Обработчик сигнала может установить флаг, который будет проверяться в цикле, чтобы он соответствующим образом прервался.источник
Ты наверное хочешь
kbhit();
//Example will loop until a key is pressed #include <conio.h> #include <iostream> using namespace std; int main() { while(1) { if(kbhit()) { break; } } }
это может работать не во всех средах. Переносимый способ - создать поток мониторинга и установить какой-либо флаг
getch();
источник
Еще один способ получить неблокирующий ввод с клавиатуры - открыть файл устройства и прочитать его!
Вы должны знать файл устройства, который вы ищете, один из / dev / input / event *. Вы можете запустить cat / proc / bus / input / devices, чтобы найти нужное устройство.
Этот код у меня работает (запускать от имени администратора).
#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; }
источник
Для этого можно использовать библиотеку curses. Конечно,
select()
и обработчики сигналов тоже в определенной степени можно использовать.источник
Если вы счастливы, просто поймав Control-C, дело сделано. Если вам действительно нужен неблокирующий ввод-вывод, но вы не хотите использовать библиотеку curses, другой альтернативой является перемещение блокировки, приклада и ствола в библиотеку AT&T
sfio
. Это хорошая библиотека, созданная по образцу C,stdio
но более гибкая, поточно-ориентированная и лучше работает. (sfio означает безопасный и быстрый ввод-вывод.)источник
Для этого нет переносимого способа, но select () может быть хорошим способом. См. Http://c-faq.com/osdep/readavail.html для получения дополнительных возможных решений.
источник
Вы можете сделать это с помощью select следующим образом:
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; } } }
Не особо много работы: D
источник
nfds
должен быть установлен на 1.Вот функция, которая сделает это за вас. Вам нужен тот,
termios.h
который поставляется с системами POSIX.#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
получает текущую информацию о терминале и сохраняет ее вt
. Еслиcmd
равно 1, флаг локального вводаt
установлен на неблокирующий ввод. В противном случае он сбрасывается. Затемtcsetattr
меняет стандартный ввод наt
.Если вы не сбросите стандартный ввод в конце своей программы, у вас будут проблемы в вашей оболочке.
источник