在Windows中,_kbhit()用来检测stdin中是否有字符输入。它既不会消费字符也不会阻塞进程,主要用途就是在交互式console程序中检测是否有key按下。
- 返回非零值 - 当stdin中有字符等待时
- 返回0 - 当stdin中没有字符等待时
但是在Linux和Mac OS中没有类似的函数。
GNU Curses提供了相同功能的函数,但是Curses在使用前,必须在main()中做初始化;之后,可以直接使用_kbhit(),不用初始化该函数了。
GNU Curses libraryhttps://invisible-island.net/ncurses/ 也可以自己写一个同样功能的函数,不用安装和连接额外的库;可以直接迁移到Linux、OS X, AIX或者其他类UNIX的OS上。
- ioctl()函数,是控制I/O驱动的低级方法。可以在Linux上实现类似_kbhit()功能,但是有可能不能迁移到其他的OS上,它的输入参数依赖所使用的stream和drivers;
- select()函数:使用该函数,是更一般化的实现;
技术实现中的问题
ioctl()和select()不能判断输入区里是否有newline输入,可以当键按下时候,直接判断是否有0字符等待,。
为解决这个问题,要先禁用line buffering,可以使用termios.h中的方法。另外一个方案是只使用ioctl()来避免使用termios,h。termios.h是大部分系统中的标准头文件,没有必要避开它。
两种实现都使用一个静态变量来检测 首次调用并禁用缓冲。stdout上的输出缓冲为仍处于启用状态。如果您想打印到stdout并查看结果 在发送换行符之前,使用命令 flush(stdout) 。
Linux 版本的 _kbhit()与 Windows版本执行相同的规范。但是实际值返回的非零整数在两个平台上会有所不同。
实现方法一:使用select()
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
void changemode(int);
int kbhit(void);
int main(void)
{
int ch;
changemode(1);
while ( !kbhit() )
{
putchar('.');
}
ch = getchar();
printf("\nGot %c\n", ch);
changemode(0);
return 0;
}
void changemode(int dir)
{
static struct termios oldt, newt;
if ( dir == 1 )
{
tcgetattr( STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~( ICANON | ECHO );
tcsetattr( STDIN_FILENO, TCSANOW, &newt);
}
else
tcsetattr( STDIN_FILENO, TCSANOW, &oldt);
}
int kbhit (void)
{
struct timeval tv;
fd_set rdfs;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&rdfs);
FD_SET (STDIN_FILENO, &rdfs);
select(STDIN_FILENO+1, &rdfs, NULL, NULL, &tv);
return FD_ISSET(STDIN_FILENO, &rdfs);
}
实现方法二:使用ioctl()
/**
Linux (POSIX) implementation of _kbhit().
Morgan McGuire, morgan@cs.brown.edu
*/
#include <stdio.h>
#include <sys/select.h>
#include <termios.h>
#include <stropts.h>
#include <sys/ioctl.h> //不加这个头文件会导致FIONREAD未定义的错误
int _kbhit() {
static const int STDIN = 0;
static bool initialized = false;
if (! initialized) {
// Use termios to turn off line buffering
termios term;
tcgetattr(STDIN, &term);
term.c_lflag &= ~ICANON;
tcsetattr(STDIN, TCSANOW, &term);
setbuf(stdin, NULL);
initialized = true;
}
int bytesWaiting;
ioctl(STDIN, FIONREAD, &bytesWaiting);
return bytesWaiting;
}
//
// Simple demo of _kbhit()
#include <unistd.h>
int main(int argc, char** argv) {
printf("Press any key");
while (! _kbhit()) {
printf(".");
fflush(stdout);
usleep(1000);
}
printf("\nDone.\n");
return 0;
}