[英]Making stdin non-blocking
我有一個練習,我需要緩慢打印文件(間隔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;
}
謝謝您的幫助!
這是一個工作版本,使用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;
}
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()s重用struct timeval時,這會導致問題重新初始化它。 在select()返回后,請考慮超時未定義。
您需要在每次調用選擇之前初始化timeval
,而不僅僅是在程序開頭的一次。
你想讓你的程序多線程。 創建一個每1秒間隔打印出文件的線程,主線程將從stdin獲取輸入,然后在獲得輸入時發出另一個線程的信號以停止打印
你的部分問題是你正在使用sleep(1)
,這會導致該行需要一整秒才能執行。 如果用戶鍵入一個字符,他們將需要等待一整秒才能在程序響應之前。 因此,即使您使非阻塞部分工作,您仍然會遇到問題。
解決方案是使用nanosleep
或usleep
暫停程序不到1秒。 我的建議是使用其中一種功能睡眠1/100秒*並每次檢查用戶按鍵。 在第100次,輸出文件的下一部分。 這樣文件仍然以正確的速度運行,但是用戶可以隨時停止它,程序將很快響應它們的命令。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.