我有一个练习,我需要缓慢打印文件(间隔1秒),直到文件结束,除非用户键入一个字符.
到目前为止,程序以一秒的间隔输出文件很好,但是当我输入一个字符时,没有任何反应.我的猜测是我以某种方式使用选择错误.
这是我最终提交的最终计划.
#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
FILE* infile;
char str[100];
fd_set readset;
struct timeval tv;
// open a file
if((infile = fopen("infile", "r")) == NULL)
{
(void)printf("Couldn't open the file\n");
exit(1);
}
// file was opened successfully
else
{
// while we are not at the end of a file
while(fgets(str, 100, infile) != NULL)
{
FD_ZERO(&readset);
FD_SET(fileno(stdin), &readset);
// set the time value to 1 second
tv.tv_sec = 1;
tv.tv_usec = 0;
select(fileno(infile)+1, &readset, NULL, NULL, &tv);
// the user typed a character so exit
if(FD_ISSET(fileno(stdin), &readset))
{
fclose(infile);
exit(0);
}
// the user didn't type a character so print the next line
else
{
fgets(str, 100, stdin);
puts(str);
}
}
// clean up
fclose(infile);
}
// report success
return 0;
}
Run Code Online (Sandbox Code Playgroud)
谢谢您的帮助!
这是一个工作版本,使用tcgetattr/tcsetattr:
#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
int main(void) {
FILE* infile;
char str[100];
fd_set readset;
struct timeval tv;
struct termios ttystate, ttysave;
// open a file
if((infile = fopen("infile", "r")) == NULL)
{
(void)printf("Couldn't open the file\n");
exit(1);
}
// file was opened successfully
//get the terminal state
tcgetattr(STDIN_FILENO, &ttystate);
ttysave = ttystate;
//turn off canonical mode and echo
ttystate.c_lflag &= ~(ICANON | ECHO);
//minimum of number input read.
ttystate.c_cc[VMIN] = 1;
//set the terminal attributes.
tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
// while we are not at the end of a file
while(fgets (str, 100, infile))
{
// set the time value to 1 second
tv.tv_sec = 1;
tv.tv_usec = 0;
FD_ZERO(&readset);
FD_SET(fileno(stdin), &readset);
select(fileno(stdin)+1, &readset, NULL, NULL, &tv);
// the user typed a character so exit
if(FD_ISSET(fileno(stdin), &readset))
{
fgetc (stdin); // discard character
break;
}
// the user didn't type a character so print the next line
else
{
puts(str);
// not needed: sleep(1);
}
}
// clean up
fclose(infile);
ttystate.c_lflag |= ICANON | ECHO;
//set the terminal attributes.
tcsetattr(STDIN_FILENO, TCSANOW, &ttysave);
// report success
return 0;
}
Run Code Online (Sandbox Code Playgroud)
将sleep(1);不再被需要.
终端正在缓冲线路。在您按下该键之前,它不会向程序发送文本Enter。可能有一种方法可以禁用终端行缓冲,但我想这超出了您的任务范围。
当您按下 时,它会停止Enter。但是,它不会立即退出。这是你想要解决的问题。摆脱那个sleep(1)。
现在你的程序正在发送垃圾短信!你给了select一秒钟的暂停时间,不是吗?
// set the time value to 1 second
tv.tv_sec = 1;
tv.tv_usec = 0;
Run Code Online (Sandbox Code Playgroud)
超时不坚持的原因是因为select正在修改超时值。从手册页:
在 Linux 上,select() 修改超时以反映未睡眠的时间量;大多数其他实现不会这样做。(POSIX.1-2001 允许这两种行为。)当读取超时的 Linux 代码移植到其他操作系统时,以及当代码移植到在循环中为多个 select() 重用 struct timeval 且不使用 struct timeval 的 Linux 时,这都会导致问题。重新初始化它。select() 返回后,考虑超时未定义。
您需要在每次timeval调用 select之前初始化,而不仅仅是在程序开始时初始化一次。