Как заставить программу на Си остановить цикл от любого пользовательского ввода?
Я хотел бы написать небольшую программу на C, которая выполняет бесконечный цикл, пока пользователь не нажмет клавишу на клавиатуре (то есть: в буфере stdin есть символ). Я столкнулся с проблемой разрыва цикла при вводе данных пользователем. Я пытался использовать fgetc
но это не ведет себя, как ожидалось. Код ниже ожидает пользовательского ввода, а не работает до пользовательского ввода.
Образец кода C:
while((c=fgetc(stdin) == EOF) {
/* Does stuff for infinite loop here */
printf("Example work in the loop\n");
}
printf("Out of the loop!\n");
Как мне написать цикл, который выполняется до вмешательства пользователя? Нажатие любой клавиши или определенной клавиши может быть триггером вмешательства.
Примечание 1: я пишу это для консоли Unix в случае решений для конкретной платформы
Примечание 2: не предлагать Ctrl + C/X/Z
как пользовательский триггер вмешательства
1 ответ
Кажется, это работает для меня:
#include <fcntl.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
static void set_non_blocking(int fd)
{
int flags = fcntl(fd, F_GETFL, 0 );
flags |= O_NONBLOCK;
flags = fcntl(fd, F_SETFL, flags);
}
int main(int argc, char ** argv)
{
int fd = fileno(stdin);
char buf[10];
set_non_blocking(fd);
while (read(fd, buf, sizeof buf) < 0) {
perror("read");
sleep(1);
}
return 0;
}
или вы могли бы использовать select
:
int main(int argc, char ** argv)
{
int fd = fileno(stdin);
struct timeval tv = {0,0};
fd_set fdset;
int s;
do {
sleep(1);
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
} while ((s = select(fd+1, &fdset, NULL, NULL, &tv)) == 0);
if (s < 0) {
perror("select");
}
return 0;
}
Опрос тоже работает:-)
int main(int argc, char ** argv)
{
struct pollfd pfd;
int s;
pfd.fd = fileno(stdin);
pfd.events = POLLRDNORM;
while ((s = poll(&pfd, 1, 0)) == 0) {
perror("polling");
sleep(1);
}
if (s < 0) {
perror("poll");
}
return 0;
}
Последний способ - установить терминал в "сырой" режим. Обратите внимание, что это нарушает вывод на терминал (по крайней мере на моем на OS-X), так как \r становится необходимым после \n. Обратите также внимание, что его необходимо отменить в конце (завершающий tcsetattr
вызов). Это единственный, который не нуждается в \n (т.е. подойдет любое нажатие клавиши)
#include <poll.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
static void set_non_blocking(int fd)
{
int flags = fcntl(fd, F_GETFL, 0) | O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) < 0) {
perror("fcntl");
exit(EXIT_FAILURE);
}
}
int main(int argc, char ** argv)
{
struct termios params;
struct termios params_orig;
char buf[10];
int fd = fileno(stdin);
if (tcgetattr(fd, ¶ms) < 0) {
perror("tcgetattr");
exit(EXIT_FAILURE);
}
params_orig = params;
cfmakeraw(¶ms);
if (tcsetattr(fd, TCSANOW, ¶ms) < 0) {
perror("tcsetattr");
exit(EXIT_FAILURE);
}
set_non_blocking(fd);
while (read(fd, buf, sizeof buf) < 0) {
perror("\rread");
sleep(1);
}
(void) tcsetattr(fd, TCSANOW, ¶ms_orig);
return 0;
}