如何在Linux/Mac OS下实现_kbhit()

文章介绍了如何在Linux和MacOS系统中模拟Windows的_kbhit()函数,用于检测键盘输入而不会阻塞进程。提供了使用select()和ioctl()两种方法,并讨论了它们的局限性和解决方案,包括禁用行缓冲和初始化终端模式。示例代码展示了具体实现。
摘要由CSDN通过智能技术生成

在Windows中,_kbhit()用来检测stdin中是否有字符输入。它既不会消费字符也不会阻塞进程,主要用途就是在交互式console程序中检测是否有key按下。

  • 返回非零值 - 当stdin中有字符等待时
  • 返回0 - 当stdin中没有字符等待时

但是在Linux和Mac OS中没有类似的函数。

GNU Curses提供了相同功能的函数,但是Curses在使用前,必须在main()中做初始化;之后,可以直接使用_kbhit(),不用初始化该函数了。

  GNU Curses libraryicon-default.png?t=N658https://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;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值