使stdin无阻塞

我有一个练习,我需要缓慢打印文件(间隔1秒),直到文件结束,除非用户键入一个字符。

到目前为止,程序以一秒的间隔输出文件,这很好,但是当我输入一个字符时,没有任何反应。 我的猜测是我以某种方式使用选择错误。

这是我最终提交的最终计划。

#include  #include  #include  #include  #include  #include  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; } 

谢谢您的帮助!

这是一个工作版本,使用tcgetattr / tcsetattr:

 #include  #include  #include  #include  #include  #include  #include  #include  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; } 

sleep(1); 不再需要了。

终端是缓冲线路。 在按Enter键之前,它不会向程序发送文本。 可能有一种方法可以禁用终端线缓冲,但我认为它超出了您的任务范围。

Enter键时停止。 但是,它不会立即退出。 这是你想要解决的问题。 摆脱那种sleep(1)

现在你的程序垃圾邮件! 你给了select超时一秒,不是吗?

 // set the time value to 1 second tv.tv_sec = 1; tv.tv_usec = 0; 

超时不坚持的原因是因为select正在修改超时值。 从手册页 :

在Linux上,select()修改超时以反映未睡眠的时间; 大多数其他实现不会这样做。 (POSIX.1-2001允许任何一种行为。)当读取超时的Linux代码移植到其他操作系统时,以及当代码移植到Linux时,在循环中为多个select()重用struct timeval时,这会导致问题重新初始化它。 在select()返回后,请考虑超时未定义。

您需要在每次调用选择之前初始化timeval ,而不仅仅是在程序开始时调用一次。

你想让你的程序multithreading。 创建一个每1秒间隔打印出文件的线程,主线程将从stdin获取输入,然后在获得输入时通知另一个线程停止打印

你的部分问题是你正在使用sleep(1) ,这会导致该行需要一整秒才能执行。 如果用户键入一个字符,他们将需要等待一整秒才能在程序响应之前。 因此,即使您使非阻塞部分工作,您仍然会遇到问题。

解决方案是使用nanosleepusleep暂停程序不到1秒。 我的建议是使用其中一个function睡眠1/100秒*并每次检查用户按键。 在第100次,输出文件的下一部分。 这样文件仍然以正确的速度运行,但是用户可以随时停止它,程序将很快响应它们的命令。