检查stdin缓冲区是否为空

我试图用字符读取一个数字字符,但我不知道stdin缓冲区是否为空。

我的第一个解决方案是在stdin缓冲区中寻找’\ n’字符,但如果我输入用”(空格)分隔的多个数字,那就不好了。

我怎么知道在stdin缓冲区中我是否有字符?

我不得不用C语言做,并且便携。

有几个词:

轮询或选择超时为0 – 这些将立即返回,如果没有数据可用,则结果为errno EAGAIN为-1或者数据描述符数量为1(因为您只检查stdin,因此结果为1)。

ioctl是使用描述符的瑞士军刀。 您需要的是I_NREAD

 if (ioctl(0, I_NREAD, &n) == 0 && n > 0) // we have exactly n bytes to read 

然而,正确的解决方案是将您获得的所有内容(使用scanf )作为一行读取,然后处理结果 – 这对sscanf效果很好:

 char buf[80]; // large enough scanf("%79s", buf); // read everything we have in stdin if (sscanf(buf, "%d", &number) == 1) // we have a number 

…只要你正确处理重读,比你的缓冲区更长的字符串,以及其他现实生活中的复杂情况。

编辑:删除feof因为它实际上用于其他事情。

我在本页的@ stek29post中引用了这一点,并编写了一个简单的例子,如下所示:

 #include  #include  #include  int main(void) { fd_set readfds; FD_ZERO(&readfds); struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; char message[50]; while(1) { FD_SET(STDIN_FILENO, &readfds); if (select(1, &readfds, NULL, NULL, &timeout)) { scanf("%s", message); printf("Message: %s\n", message); } printf("...\n"); sleep(1); } return(0); } 

对于从谷歌来这里的任何人 – 轻松select解决方案来检查stdin空白:

 fd_set readfds; FD_ZERO(&readfds); FD_SET(STDIN_FILENO, &readfds); fd_set savefds = readfds; struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; int chr; int sel_rv = select(1, &readfds, NULL, NULL, &timeout); if (sel_rv > 0) { puts("Input:"); while ((chr = getchar()) != EOF) putchar(chr); } else if (sel_rv == -1) { perror("select failed"); } readfds = savefds; 

需要unistd.hstdlib.hstdio.h

可以在这里找到解释。

UPD:感谢DrBeco注意到错误时选择返回-1 – 添加了error handling。

实际上,选择返回:

  • 描述符集中包含的就绪描述符的数量
  • 如果时间限制到期,则为0
  • -1如果发生错误(将设置errno)

有很多方法可以检查stdin是否有可用的输入。 最便携的是,按顺序: selectfcntlpoll

这里有一些关于如何做的片段,视具体情况而定。

 #include  /* same old */ #include  /* same old */ #include  /* struct timeval for select() */ #include  /* select() */ #include  /* poll() */ #include  /* FIONREAD ioctl() */ #include  /* tcgetattr() and tcsetattr() */ #include  /* fnctl() */ #define BUFF 256 int chkin_select(void); int chkin_poll(void); int chkin_ioctl(void); int chkin_fcntl(void); int chkin_termios(void); /* Simple loops to test varios options of non-blocking test for stdin */ int main(void) { char sin[BUFF]="r"; printf("\nType 'q' to advance\nTesting select()\n"); while(sin[0]++ != 'q') { while(!chkin_select()) { printf("nothing to read on select()\n"); sleep(2); } fgets(sin, BUFF, stdin); printf("\nInput select(): %s\n", sin); } printf("\nType 'q' to advance\nTesting poll()\n"); while(sin[0]++ != 'q') { while(!chkin_poll()) { printf("nothing to read poll()\n"); sleep(2); } fgets(sin, BUFF, stdin); printf("\nInput poll(): %s\n", sin); } printf("\nType 'q' to advance\nTesting ioctl()\n"); while(sin[0]++ != 'q') { while(!chkin_ioctl()) { printf("nothing to read ioctl()\n"); sleep(2); } fgets(sin, BUFF, stdin); printf("\nInput ioctl(): %s\n", sin); } printf("\nType 'q' to advance\nTesting fcntl()\n"); while(sin[0]++ != 'q') { while(!chkin_fcntl()) { printf("nothing to read fcntl()\n"); sleep(2); } fgets(sin, BUFF, stdin); printf("\nInput fcntl: %s\n", sin); } printf("\nType 'q' to advance\nTesting termios()\n"); while(sin[0]++ != 'q') { while(!chkin_termios()) { printf("nothing to read termios()\n"); sleep(2); } fgets(sin, BUFF, stdin); printf("\nInput termios: %s\n", sin); } return EXIT_SUCCESS; } /* select() and pselect() allow a program to monitor multiple file descriptors, waiting until one or more of the file descriptors become "ready" for some class of I/O operation (eg, input possible). A file descriptor is considered ready if it is possible to perform a corresponding I/O operation (eg, read(2) without blocking, or a sufficiently small write(2)). */ int chkin_select(void) { fd_set rd; struct timeval tv={0}; int ret; FD_ZERO(&rd); FD_SET(STDIN_FILENO, &rd); ret=select(1, &rd, NULL, NULL, &tv); return (ret>0); } /* poll() performs a similar task to select(2): it waits for one of a set of file descriptors to become ready to perform I/O. The set of file descriptors to be monitored is specified in the fds argument, which is an array of structures of the following form: struct pollfd { int fd; // file descriptor // short events; // requested events // short revents; // returned events // }; The caller should specify the number of items in the fds array in nfds. */ int chkin_poll(void) { int ret; struct pollfd pfd[1] = {0}; pfd[0].fd = STDIN_FILENO; pfd[0].events = POLLIN; ret = poll(pfd, 1, 0); return (ret>0); } /* The ioctl(2) call for terminals and serial ports accepts many possible command arguments. Most require a third argument, of varying type, here called argp or arg. Use of ioctl makes for nonportable programs. Use the POSIX interface described in termios(3) whenever possible. */ int chkin_ioctl(void) { int n; ioctl(STDIN_FILENO, FIONREAD, &n); return (n>0); } /* fcntl() performs one of the operations described below on the open file descriptor fd. The operation is determined by cmd. fcntl() can take an optional third argument. Whether or not this argument is required is determined by cmd. The required argument type is indicated in parentheses after each cmd name (in most cases, the required type is int, and we identify the argument using the name arg), or void is specified if the argument is not required. Certain of the operations below are supported only since a particular Linux kernel version. The preferred method of checking whether the host kernel supports a particular operation is to invoke fcntl() with the desired cmd value and then test whether the call failed with EINVAL, indicating that the kernel does not recognize this value. */ int chkin_fcntl(void) { int flag, ch; flag = fcntl(STDIN_FILENO, F_GETFL, 0); /* save old flags */ fcntl(STDIN_FILENO, F_SETFL, flag|O_NONBLOCK); /* set non-block */ ch = ungetc(getc(stdin), stdin); fcntl(STDIN_FILENO, F_SETFL, flag); /* return old state */ return (ch!=EOF); } /* The termios functions describe a general terminal interface that is provided to control asynchronous communications ports. This function doesn't wait for '\n' to return! */ int chkin_termios(void) { struct termios old, new; int ch; tcgetattr(STDIN_FILENO, &old); /* save settings */ new = old; new.c_lflag &= ~ICANON; /* non-canonical mode: inputs by char, not lines */ new.c_cc[VMIN] = 0; /* wait for no bytes at all */ new.c_cc[VTIME] = 0; /* timeout */ tcsetattr(STDIN_FILENO, TCSANOW, &new); /* new settings */ ch = ungetc(getc(stdin), stdin); /* check by reading and puking it back */ tcsetattr(STDIN_FILENO, TCSANOW, &old); /* restore old settings */ return (ch!=EOF); } 

尽量避免使用ioctltermios ,它们太具体,或者太低级别。 此外,对于stdin或任何FIFO,你无法以有意义的方式使用feof 。 您可以保证指针位置,如果您尝试ftellfseek您将收到错误(询问perror )。


参考文献: