我想在控制台应用程序中完成以下任务:
1
,则程序
将执行任务1,如果用户输入q
,程序将退出; 这是我的代码:
#include <stdio.h>
#include <time.h>
char buff[64];
char command;
while(command != 'q')
{
begin:
printf(">> ");
scanf("%s", buff);
command = buff[0];
switch (command)
{
case '1':
// task 1 code will be added here;
break;
case '2':
// task 2 code will be added here;
break;
case 'q':
printf("quit the loop.\n");
break;
}
// wait for 10 seconds;
Sleep(10000);
// default task code will be added here;
if(command != 'q')
{
goto begin;
}
}
但问题是,如果没有输入字符,程序将永远陷入
scanf()
函数行等待输入字符。所以我想知道如何在一定时间后跳过scanf()
行,我的意思是,例如,如果1秒后没有输入,程序可以继续,从而完成上面列出的第二件事。
如果重要的话,平台是 Windows。
我删除了
while()
后面的分号,这是一个明显的错误。
尝试使用 select() 函数。然后你可以等待 10 秒,直到你可以从 stdin 读取而不阻塞。如果 select() 返回零,则执行默认操作。 我不知道这是否适用于 Windows,这是 POSIX 标准。如果你碰巧在unix/linux上开发,请尝试
man select
我刚刚使用 select 编写了一个工作示例:
#include <stdlib.h>
#include <stdio.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#define MAXBYTES 80
int main(int argc, char *argv[])
{
fd_set readfds;
int num_readable;
struct timeval tv;
int num_bytes;
char buf[MAXBYTES];
int fd_stdin;
fd_stdin = fileno(stdin);
while(1) {
FD_ZERO(&readfds);
FD_SET(fileno(stdin), &readfds);
tv.tv_sec = 10;
tv.tv_usec = 0;
printf("Enter command: ");
fflush(stdout);
num_readable = select(fd_stdin + 1, &readfds, NULL, NULL, &tv);
if (num_readable == -1) {
fprintf(stderr, "\nError in select : %s\n", strerror(errno));
exit(1);
}
if (num_readable == 0) {
printf("\nPerforming default action after 10 seconds\n");
break; /* since I don't want to test forever */
} else {
num_bytes = read(fd_stdin, buf, MAXBYTES);
if (num_bytes < 0) {
fprintf(stderr, "\nError on read : %s\n", strerror(errno));
exit(1);
}
/* process command, maybe by sscanf */
printf("\nRead %d bytes\n", num_bytes);
break; /* to terminate loop, since I don't process anything */
}
}
return 0;
}
注意:下面的 poll() 示例也可以,没有问题。对于其余部分,我选择将可用字节读入缓冲区(最多 MAXBYTES)。之后可以对其进行扫描。 (scanf() 只是不太适合我的朋友,但这是个人品味问题)。
这是一个如何使用
poll
执行此操作的示例(可能是 Linux 上最“正确”的方法):
#include <unistd.h>
#include <poll.h>
#include <stdio.h>
int main()
{
struct pollfd mypoll = { STDIN_FILENO, POLLIN|POLLPRI };
char string[10];
if( poll(&mypoll, 1, 2000) )
{
scanf("%9s", string);
printf("Read string - %s\n", string);
}
else
{
puts("Read nothing");
}
return 0;
}
超时是
poll
的第三个参数,以毫秒为单位 - 此示例将等待 2 秒以等待标准输入上的输入。 Windows 有 WSAPoll
,其工作原理应该类似。
但问题是程序会永远陷入 scanf() 函数所在行等待输入字符,
删除
while
后面的分号。
尝试闹钟(3)
#include <stdio.h>
#include <unistd.h>
int main(void)
{
char buf [10];
alarm(3);
scanf("%s", buf);
return 0;
}
正如其他人所说,实现真正异步 IO 的最佳方法是使用
select(...)
。
但是做你想要的事情的一种快速而肮脏的方法是使用
getline(...)
它将返回每次读取的字节数(不挂在 IO 上)并在没有读取字节时返回 -1。
以下内容来自 getline(3) 手册页:
// The following code fragment reads lines from a file and writes them to standard output.
// The fwrite() function is used in case the line contains embedded NUL characters.
char *line = NULL;
size_t linecap = 0;
ssize_t linelen;
while ((linelen = getline(&line, &linecap, fp)) > 0)
fwrite(line, linelen, 1, stdout);
不幸的是,您所要求的在普通的 ISO C 中是不可能的。但是,大多数平台都提供特定于平台的扩展,这些扩展可以提供您所需的功能。
ncurses
库。
既然您说您的问题适用于 Microsoft Windows,那么您正在寻找的两个功能是
WaitForSingleObject
和 ReadConsoleInput
。为此,您将无法使用函数 scanf
或 C 风格流 (FILE*
)。
函数
WaitForSingleObject
允许您等待,直到特定对象处于有信号状态。它还允许您设置超时(在您的情况下应该是 10 秒)。
WaitForSingleObject
可以等待的一种对象是控制台句柄。当有新的输入可供 ReadConsoleInput
读取时,它将处于有信号状态。根据我的测试,它不能与 ReadConsole
一起可靠地工作,因为即使 WaitForSingleObject
指示输入正在等待,函数 ReadConsole
有时仍然会阻塞。这是因为与 ReadConsoleInput
相比,函数 ReadConsole
会过滤一些事件。因此,尝试使用 ReadConsole
读取一个事件实际上可能会尝试读取多个原始事件,如果没有可用的未过滤原始事件,这将导致该函数阻塞。函数 ReadConsoleInput
没有这个问题,因为它直接处理原始事件并且不过滤任何事件。
这是我的程序,它使用上面提到的功能,并且完全按照您的要求进行操作。
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
void DoDefaultTask()
{
printf( "Doing default task.\n" );
}
void DoTask1()
{
printf( "Doing task #1.\n" );
}
void DoTask2()
{
printf( "Doing task #2.\n" );
}
int main(void)
{
INPUT_RECORD ir;
HANDLE hConInput = GetStdHandle(STD_INPUT_HANDLE);
DWORD dwReadCount;
bool should_prompt = true, quit = false;
while ( !quit )
{
//prompt user for input
if ( should_prompt )
{
printf( "Please select an option: " );
should_prompt = false;
}
//flush output
fflush( stdout );
switch ( WaitForSingleObject( hConInput, 10000 ) )
{
case WAIT_OBJECT_0:
//attempt to read input
if ( !ReadConsoleInput( hConInput, &ir, 1, &dwReadCount ) || dwReadCount != 1 )
{
fprintf( stderr, "Unexpected input error!\n" );
exit( EXIT_FAILURE );
}
//only handle key-down events
if ( ir.EventType != KEY_EVENT || !ir.Event.KeyEvent.bKeyDown )
break;
//echo output, if character is printable
if ( isprint( (unsigned char)ir.Event.KeyEvent.uChar.AsciiChar ) )
{
printf( "%c", ir.Event.KeyEvent.uChar.AsciiChar );
}
printf( "\n" );
switch ( ir.Event.KeyEvent.uChar.AsciiChar )
{
case '1':
DoTask1();
break;
case '2':
DoTask2();
break;
case 'q':
printf( "Quitting program...\n" );
quit = true;
break;
default:
printf( "Unknown command.\n" );
}
should_prompt = true;
break;
case WAIT_TIMEOUT:
printf( "Timeout!\n" );
DoDefaultTask();
should_prompt = true;
break;
default:
fprintf( stderr, "unexpected error!" );
exit( EXIT_FAILURE );
}
}
return 0;
}
该程序具有以下行为:
Please select an option: 1
Doing task #1.
Please select an option: 2
Doing task #2.
Please select an option: 3
Unknown command.
Please select an option: 4
Unknown command.
Please select an option: Timeout!
Doing default task.
Please select an option: Timeout!
Doing default task.
Please select an option: 1
Doing task #1.
Please select an option: 3
Unknown command.
Please select an option: 2
Doing task #2.
Please select an option: 1
Doing task #1.
Please select an option: Timeout!
Doing default task.
Please select an option: q
Quitting program...