检查 stdin 缓冲区是否为空



我正在尝试读取带有字符的数字字符,但我不知道 stdin 缓冲区是否为空。

我的第一个解决方案是在 stdin 缓冲区中查找n字符,但如果我输入由 " " 分隔的多个数字,这不好。

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

我需要用 C 语言完成并且是可移植的。

有以下几种说法:

轮询或选择 超时为 0 - 这些将立即返回,结果要么是 -1,带有 errno EAGAIN如果没有可用数据,则为包含数据的描述符数量(一个,因为您只检查 stdin)。

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

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

对于从谷歌来到这里的任何人 - 简单的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

可以在此处找到解释。

上级:感谢 DrBeco 注意到选择在错误时返回 -1 - 添加了错误处理。

实际上,选择退货:

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

我从这个页面上的@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: %sn", message);
        }
        printf("...n");
        sleep(1);
    }
    return(0);
}

有很多方法可以检查stdin是否有可用的输入。最便携的依次是: selectfcntlpoll .

这里有一些关于如何做到这一点的片段,逐案处理。

#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 advancenTesting 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(): %sn", sin);
    }
    printf("nType 'q' to advancenTesting poll()n");
    while(sin[0]++ != 'q')
    {
        while(!chkin_poll())
        {
            printf("nothing to read poll()n");
            sleep(2);
        }
        fgets(sin, BUFF, stdin);
        printf("nInput poll(): %sn", sin);
    }
    printf("nType 'q' to advancenTesting ioctl()n");
    while(sin[0]++ != 'q')
    {
        while(!chkin_ioctl())
        {
            printf("nothing to read ioctl()n");
            sleep(2);
        }
        fgets(sin, BUFF, stdin);
        printf("nInput ioctl(): %sn", sin);
    }
    printf("nType 'q' to advancenTesting fcntl()n");
    while(sin[0]++ != 'q')
    {
        while(!chkin_fcntl())
        {
            printf("nothing to read fcntl()n");
            sleep(2);
        }
        fgets(sin, BUFF, stdin);
        printf("nInput fcntl: %sn", sin);
    }
    printf("nType 'q' to advancenTesting termios()n");
    while(sin[0]++ != 'q')
    {
        while(!chkin_termios())
        {
            printf("nothing to read termios()n");
            sleep(2);
        }
        fgets(sin, BUFF, stdin);
        printf("nInput termios: %sn", 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);
}

尽量避免ioctltermios,它们太具体,或者太低级。此外,您不能真正以有意义的方式将feof与 stdin 或任何 FIFO 一起使用。您可以保证指针位置,如果您尝试ftellfseek,则会出现错误(询问perror)。


引用:

  • http://man7.org/linux/man-pages/man2/select.2.html
  • http://man7.org/linux/man-pages/man2/poll.2.html
  • http://man7.org/linux/man-pages/man2/ioctl.2.html
  • http://man7.org/linux/man-pages/man2/fcntl.2.html
  • http://man7.org/linux/man-pages/man3/termios.3.html
int number=-1;   //-1 is default std for error;
int success=0;   //will serve as boolean (0==FALSE;1==TRUE)
char buf[BUFFER_SIZE];   // Define this as convinient (e.g. #define BUFFER_SIZE 100)
char *p=buf;   //we'll use a pointer in order to preserve input, in case you want to use it later
    fgets(buf,BUFFER_SIZE,stdin);  //use fgets() for security AND to grab EVERYTHING from stdin, including whitespaces
    while(*p!=''){   //parse the buf
        if(sscanf(p,"%d",&number)==1){   //at each char position try to grab a valid number format, 
            success=1;                   //if you succeed, then flag it.
            break;
        }
        
        p++;   //if you don't succeed, advance the pointer to the next char position
     }   //repeat the cycle until the end of buf (string end char =='')
    if (success)
        printf(">> Number=%d at position nº %d.",number,(int)(p-buf));   //you get the position by calculating the diff 
                                                                    //between the current position of the p and the 
                                                                    //beginning position of the buf
    else {
    // do whatever you want in case of failure at grabbing a number
    }

相关内容

最新更新