很遗憾,您所要求的在普通的ISO C中是不可能实现的。然而,大多数平台都提供了特定于平台的扩展功能,可以提供您需要的功能。
由于您说明您的问题适用于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 )
{
if ( should_prompt )
{
printf( "Please select an option: " );
should_prompt = false;
}
fflush( stdout );
switch ( WaitForSingleObject( hConInput, 10000 ) )
{
case WAIT_OBJECT_0:
if ( !ReadConsoleInput( hConInput, &ir, 1, &dwReadCount ) || dwReadCount != 1 )
{
fprintf( stderr, "Unexpected input error!\n" );
exit( EXIT_FAILURE );
}
if ( ir.EventType != KEY_EVENT || !ir.Event.KeyEvent.bKeyDown )
break;
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...
poll
或select
来查看STDIN_FILENO上是否发生了什么。 - hetepeperfan