C语言 检查标准输入缓冲区是否为空
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26948723/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Checking the stdin buffer if it's empty
提问by Tandura
I am trying to read a number character with character, but I don't know if the stdin buffer is empty or not.
我正在尝试使用字符读取数字字符,但我不知道 stdin 缓冲区是否为空。
My first solution whas to look for '\n' character in stdin buffer, but this is no good if I what to enter multiple numbers separated with ' '(blank space).
我的第一个解决方案是在 stdin 缓冲区中查找 '\n' 字符,但是如果我输入多个以 ' '(空格)分隔的数字,这并不好。
How can I know if in stdin buffer I have characters or not?
我怎么知道在标准输入缓冲区中是否有字符?
I nead to do it in C and to be portable.
我需要在 C 中完成它并且是可移植的。
采纳答案by aragaer
There are several soutions:
有几种说法:
pollor selectwith timeout of 0 - these would return immediately and result is either -1 with errno EAGAINif no data available or number of descriptors with data (one, since you're checking only stdin).
轮询或选择超时为 0 - 这些将立即返回,EAGAIN如果没有可用数据,则结果为 -1 和 errno ,或者有数据的描述符数量(一个,因为您只检查标准输入)。
ioctlis a swiss army knife of using descriptors. The request you need is I_NREAD:
ioctl是使用描述符的瑞士军刀。您需要的请求是I_NREAD:
if (ioctl(0, I_NREAD, &n) == 0 && n > 0)
// we have exactly n bytes to read
However the correct solution is to read everything you got (using scanf) as a line, then process the result - and this works good enough with sscanf:
然而,正确的解决方案是将您得到的所有内容(使用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
... as long as you properly handle re-reading, strings that are longer than your buffer, and other real-life complications.
...只要您正确处理重读、比缓冲区长的字符串以及其他现实生活中的并发症。
Edit: removed feofsince it is actually used for other things.
编辑:删除,feof因为它实际上用于其他事情。
回答by stek29
For anyone who comes here from google – easy selectsolution to check stdinemptyness:
对于从谷歌来到这里的任何人 -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;
Needs unistd.h, stdlib.hand stdio.h.
需要unistd.h,stdlib.h和stdio.h。
Explanation can be found here.
可以在此处找到说明。
UPD: Thanks DrBeco for noticing that select returns -1 on error -- error handling was added.
UPD:感谢 DrBeco 注意到 select 在错误时返回 -1 - 添加了错误处理。
Actually, select returns:
实际上,选择返回:
- the number of ready descriptors that are contained in the descriptor sets
- 0 if the time limit expires
- -1 if an error occurred (errno would be set)
- 描述符集中包含的就绪描述符的数量
- 0 如果时间限制到期
- -1 如果发生错误(将设置 errno)
回答by ccerhan
I inspired from thisas referenced by @stek29's poston this page and prepared a simple example as follows:
我从这个页面上@stek29 的帖子中引用了这一点,并准备了一个简单的例子,如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
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);
}
回答by Dr Beco
There are many ways to check if stdinhas input available. The most portable ones are, in that order: select, fcntland poll.
有很多方法可以检查是否stdin有可用的输入。最便携的是,按顺序是:select,fcntl和poll。
Here some snippets on how to do it, case by case.
这里有一些关于如何做到这一点的片段,逐案。
#include <stdio.h> /* same old */
#include <stdlib.h> /* same old */
#include <time.h> /* struct timeval for select() */
#include <unistd.h> /* select() */
#include <poll.h> /* poll() */
#include <sys/ioctl.h> /* FIONREAD ioctl() */
#include <termios.h> /* tcgetattr() and tcsetattr() */
#include <fcntl.h> /* 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 (e.g., input possible). A
file descriptor is considered ready if it is possible to perform a
corresponding I/O operation (e.g., 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);
}
Try to avoid ioctland termios, they are too specific, or too low-level. Also, you can't really use feofin a meaningful way with stdin or any FIFO for that matter. You can guarantee the pointer position, and if you try ftellor fseekyou will get an error (ask perror).
尽量避免ioctl和termios,它们太具体,或者太低级。此外,您不能真正feof以有意义的方式使用 stdin 或任何 FIFO。你可以保证指针位置,如果你尝试ftell或者fseek你会得到一个错误(询问perror)。
References:
参考:

