Tandura
Tandura

Reputation: 908

Checking the 'stdin' buffer if it's empty

I am trying to read a number character by character, but I don't know if the stdin buffer is empty or not.

My first solution was to look for '\n' character in the stdin buffer, but this isn't any good if I want to enter multiple numbers separated by spaces.

How can I know if I have characters in the stdin buffer or not?

I need to do it in C and need it to be portable.

Upvotes: 13

Views: 52493

Answers (5)

user12566332
user12566332

Reputation:

Use:

int number = -1;  // -1 is default standard for error;
int success = 0;  // Will serve as boolean (0 == FALSE; 1 == TRUE)
char buf[BUFFER_SIZE];  // Define this as convenient (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 != '\0') {  // Parse the buffer
    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 == '\0')

if (success)

    // You get the position by calculating the diff
    // between the current position of the p and the
    // beginning position of the buf
    printf(">> Number=%d at position number %d.", number, (int)(p-buf));

else {
    // Do whatever you want in case of
    // failure at grabbing a number
}

Upvotes: -1

DrBeco
DrBeco

Reputation: 11775

There are many ways to check if standard input has input available. The most portable ones are, in that order: select, fcntl and poll.

Here are 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 various options of
  nonblocking test for standard input
*/

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 ioctl and termios; they are too specific, or too low-level. Also, you can't really use feof in a meaningful way with stdin or any FIFO for that matter. You can guarantee the pointer position, and if you try ftell or fseek you will get an error (ask perror).


References (Linux manual pages):

Upvotes: 0

stek29
stek29

Reputation: 395

Here is an easy select solution to check stdin emptiness:

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;

It needs unistd.h, stdlib.h and stdio.h.

An explanation can be found here.

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)

Upvotes: 4

aragaer
aragaer

Reputation: 17848

For plain descriptors, there are several solutions:

poll or select with timeout of 0. These would return immediately and result is either -1 with errno EAGAIN if no data is available or the number of descriptors with data (one, since you're checking only standard input).

ioctl is a Swiss Army knife of using descriptors. You need the request 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, and then process the result. And this works good enough with sscanf:

char buf[80]; // Large enough
scanf("%79s", buf); // Read everything we have in standard input
if (sscanf(buf, "%d", &number) == 1)
    // We have a number

... as long as you properly handle rereading strings that are longer than your buffer, and other real-life complications. This solution also works with actual FILE * streams, while poll/select/ioctl can only handle file descriptors.

Upvotes: 11

ccerhan
ccerhan

Reputation: 672

I was inspired from this as referenced by @stek29's post on this page and prepared a simple example as follows:

#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);
}

Upvotes: 1

Related Questions