我正在写一个函数,如果一个字符已经存在于标准input中,它将从标准input读取单个字符。 如果标准input为空,则该函数假设不执行任何操作并返回-1。 我googled非阻塞input,并被指向poll()或select() 。 首先,我尝试使用select(),但我无法得到它的工作,所以我尝试了poll(),并得出了相同的结论。 我不确定这些函数做了什么,但是从我所了解的poll()的文档来看,如果我这样称呼:
struct pollfd pollfds; pollfds = STDIN_FILENO; pollfds.events = POLLIN; poll(pollfds, 1, 0);
如果(pollfds.revents&POLLIN)将为真,如果“高优先级数据以外的数据可以不被阻塞地读取”。 但是poll()总是在我的testing情况下超时。 我如何testingfunction可能是问题,但我想要的function正是我所testing的。 这里是目前的function和testing情况。
#include <poll.h> #include <stdio.h> #include <unistd.h> int ngetc(char *c) { struct pollfd pollfds; pollfds.fd = STDIN_FILENO; pollfds.events = POLLIN; poll(&pollfds, 1, 0); if(pollfds.revents & POLLIN) { //Bonus points to the persons that can tell me if //read() will change the value of '*c' if an error //occurs during the read read(STDIN_FILENO, c, 1); return 0; } else return -1; } //Test Situation: //Try to read a character left in stdin by an fgets() call int main() { int ret = 0; char c = 0; char str[256]; //Make sure to enter more than 2 characters so that the excess //is left in stdin by fgets() fgets(str, 2, stdin); ret = ngetc(&c); printf("ret = %i\nc = %c\n", ret, c); return 0; }
你正在做错误的IO,POSIX手册和所有其他相关的文档明确说,永远不要混淆IO在FILE *
和文件描述符上完成。 你已经非常公然地破坏了这条规则。 这个规则是有原因的,因为FILE *
使用了缓冲 ,这意味着在调用fgets
之后, read
将不会被read
,因为fgets
已经将所有待处理的数据读入保存在FILE *
结构中的缓冲区中。
因此,由于没有办法检查ISO C IO方法是否会被阻塞,我们只能使用文件描述符。
既然我们知道STDIN_FILENO
只是数字0,我们可以使用
fcntl (0, F_SETFL, O_NONBLOCK);
这将把文件描述符0上的所有read
变成非阻塞模式,如果你想使用不同的文件描述符,那么你可以只留下0,然后用dup
复制它。
这样,你可以完全远离poll
并实施ngetc
作为
ssize_t ngetc (char *c) { return read (0, c, 1); }
或更好,宏观
#define ngetc(c) (read (0, (c), 1))
因此,你可以简单的实现你正在寻找的东西。
编辑:如果您仍然担心终端缓冲输入,您可以随时更改终端的设置,请参阅如何禁用xterm程序中的输入行缓冲? 有关如何执行此操作的更多信息。
编辑:一个人不能使用fgetc
而不是read
的原因是因为使用fgets
将无法正常工作。 当其中一个FILE *
IO函数运行时,它从关联的文件描述符中读取所有的数据。 但是,一旦发生这种情况, poll
将永远不会返回,因为它正在等待一个始终为空的文件描述符,并且在read
也会发生同样的情况。 因此,我建议你按照文档的建议, 不要混合流 (IO使用fgets
, fgetc
等)和文件描述符 (使用read
, write
等IO)
你的代码有两个问题。
根据poll
手册 ,分配0超时将立即返回
如果timeout的值是0,poll()应该立即返回。 如果超时值是-1,则poll()应该阻塞,直到请求的事件发生或直到呼叫被中断为止。
fgets
不会做你所期望的,它是从stdio库并且将缓冲区读取。 假设你输入3个字母并按回车,在fgets
之后,第三个字母将不可用于poll
。
所以注释掉fgets行,并在poll
中将-1赋值为timeout,然后再次运行以查看是否您想要的。