一种Windows/Linux系统下的非阻塞输入方案

什么是非阻塞输入

程序获取输入的方式有很多,如 scanf()、getchar()、getche()、getch() 等函数,这些函数在执行的时候,会等待用户进行输入,直到获取输入完毕才会执行后面的代码。如果用户一直不输入,那么程序就一直等待,函数后面的代码得不到执行,这就是阻塞输入。
非阻塞输入是指:用户输入数据后程序可以捕获,用户不输入数据程序也可以继续执行。

应用场景

初学者使用的一般是阻塞式输入,这种方式一般没有任何问题,用户输入完数据再执行后面的代码往往也符合逻辑。
然而在一些情境下面,程序并不想一直等待用户输入,比如在等待输入的同时要处理一些其他事情,或者用户未必会进行输入,或者用户输入时间根本无法确定等等。最典型的例子就是小游戏中,用键盘控制物体的运动,这种情况下一定不能用阻塞输入。

实现原理

在 Windows 系统中,conio.h头文件中的kbhit()函数就可以用来实现非阻塞式键盘监听。
conio.h 是 Windows 下特有的头文件,所以 kbhit() 也只适用于 Windows,不适用于 Linux 和 Mac OS。
用户每按下一个键,都会将对应的字符放到输入缓冲区中,kbhit() 函数会检测缓冲区中是否有数据,如果有的话就返回非 0 值,没有的话就返回 0 值。但是 kbhit() 不会读取数据,数据仍然留在缓冲区,所以一般情况下我们还要结合输入函数将缓冲区种的数据读出。有了kbhit()函数,就可以判断当前是否有键盘输入,如果有那就读取,没有就继续执行其他代码。
在Linux系统中,实现的原理也是一样的,等待一个极短的时间,如果有输入那就获取字符,没有则跳过继续执行。这个过程使用select函数来实现:把标准输入(文件描述符0)添加到待读取文件集合中,如果该集合中有文件可以读取,则select函数会返回一个大于零的值,此时就可以调用函数getchar()进行读取了。

源码分享

input.h 文件:

#ifndef INPUT_H
#define INPUT_H

#if _WIN32
#include <conio.h>
#elif __linux__
#include <termio.h>
#define TTY_PATH            "/dev/tty"
#define STTY_US             "stty raw -echo -F"
#define STTY_DEF            "stty -raw echo -F"
#endif

#define BUFF_SIZE 32

int getInputStr(char inputStr[], int length);

#endif

input.c 文件:

#include "input.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h> 

static char myGetChar(void); 

int getInputStr(char inputStr[], int length)
{
    static char s_inputBuff[BUFF_SIZE] = {0}; //输入缓冲区
    static int s_inputLen = 0;                //已获得输入的长度
    int getLen = 0;                           //实际提交的长度
    char inputChar = -1;                      //当前输入字符

    if (length > BUFF_SIZE - 1)
    {
        memset(inputStr, 0, length);
        printf("\r\n  system error ! \r\n");
        exit(-1); //严重错误
    } 
    inputChar = myGetChar();
    switch (inputChar)
    {
    case -1:
        break;
    case ' ':
    case '\r':
    case '\n':
        getLen = s_inputLen; //提前获取
        s_inputBuff[getLen] = '\0';
        break;
    default:
        s_inputBuff[s_inputLen++] = inputChar;
        break;
    }
    if (s_inputLen >= length)
    {
        getLen = length; //获得了指定长度
        s_inputBuff[getLen] = '\0';
    }
    if (getLen > 0)
    {
        memcpy(inputStr, s_inputBuff, getLen + 1); //复制需要获取的长度和‘\0’
        memset(s_inputBuff, 0, BUFF_SIZE);
        s_inputLen = 0;
        return getLen;
    }
    return 0;
}

static char myGetChar(void)
{
    char ch = -1;
#if _WIN32
    if (kbhit()) //检测缓冲区中是否有数据
    {
        ch = getche(); //将缓冲区中的数据以字符的形式读出
    }
#elif __linux__
    fd_set rfds;
    struct timeval tv;
    system(STTY_US TTY_PATH);
    FD_ZERO(&rfds);
    FD_SET(0, &rfds);
    tv.tv_sec = 0;
    tv.tv_usec = 10;
    if (select(1, &rfds, NULL, NULL, &tv) > 0)
    {
        ch = getchar();
    }
#endif
    return ch;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值