13

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

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 by " ".

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

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

Neuron
  • 5,141
  • 5
  • 38
  • 59
Tandura
  • 866
  • 2
  • 7
  • 19
  • 1
    Check for EOF. (It's not actually a character, but it will be the value returned if the stream is empty.) – UncleO Nov 15 '14 at 17:51
  • 1
    Do not check if `stdin` is empty. Read a character, check the status of the operation. If status is not "OK" assume `stdin` is empty ... `if ((c = getchar()) == EOF) /*assume stdin empty; in reality could be some other reason*/;` – pmg Feb 09 '21 at 17:43

5 Answers5

11

There are several soutions:

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

ioctl is a swiss army knife of using descriptors. The request you need is 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:

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.

Neuron
  • 5,141
  • 5
  • 38
  • 59
aragaer
  • 17,238
  • 6
  • 47
  • 49
  • 2
    if I'm trying something like this `while(!feof(stdin)) { c = getchar(); putchar(c); }` whill enter an infinite loop – Tandura Nov 15 '14 at 18:39
  • functions `poll` / `select` / `ioctl` won't work eather because I nead a new library that is not included in a standard IDE (Code::Blocks that I use in college) so the solution remain the third one reading it with a buff and checking if the buf lenght is 80 (in this case) then I have to keep reading – Tandura Nov 15 '14 at 18:49
  • 1
    If your stdin is read from console, it will be at EOF only if you send EOF-character which is `ctrl+d` as far as I know. `poll`, `select`, `ioctl` are all POSIX functions and ... it would be off-topic to discuss if it should be on any system. `sscanf` solution on other hand should work as is. – aragaer Nov 15 '14 at 19:55
  • 1
    `feof` does not test whether the input is empty, but whether an end-of-input condition has occurred on a prior input operation. `feof` shouldn't be called until an input operation has been tried and has indicated failure. – Kaz Nov 01 '18 at 13:55
  • Right. I've removed the part about `feof` since for STDIN it will only return true if STDIN is closed _and_ you've read everything from it _and_ tried again. – aragaer Nov 01 '18 at 18:10
  • If you are just discarding the characters, no need to write them to `buf` - use a non-assigning conversion, such as `%*s` (which conveniently also eliminates the length constraint, too). – Toby Speight Sep 30 '19 at 09:23
  • The above `ioctl()` solution didn't work for me as is. Instead, I used [`#include `](https://www.man7.org/linux/man-pages/man2/ioctl.2.html) and `ioctl(0, FIONREAD, &n)`. Notice however, that this will only work with interactive stdin. I.e. if you pipe some data to your program it is not interactive; only if you are directly typing in the terminal. the [`expect`](https://linux.die.net/man/1/expect) can be used to provide interactive input programmatically. – Emoun Aug 07 '20 at 08:47
4

For anyone who comes here from google – easy select solution to check stdin emptyness:

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.h and stdio.h.

Explanation can be found here.

UPD: Thanks DrBeco for noticing that select returns -1 on error -- error handling was added.

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)
stek29
  • 395
  • 4
  • 14
  • works on linux but not on windows (it needs to be portable) – Tandura Jul 15 '16 at 11:36
  • 1
    @Tandura on any POSIX compatible system. Usually on Windows you have to use specific methods that won't work on other systems. So I think any *nix is better than just Windows. But yes, it's not completely portable. Idk how to do it on Windows, maybe you know? – stek29 Jul 15 '16 at 19:10
  • 1
    Remember select returns -1 when in error (which is also a `true` value) – DrBeco Jun 27 '18 at 18:58
  • 2
    If the point of checking via `select()` is to avoid hanging, the posted code will hang if there is more input sent than `stdin` can buffer in one underlying `read()` call on `STDIN_FILENO`. Also, the posted code does not detect if there is already unread data available to be read buffered in the `stdin` buffer. – Andrew Henle Jun 28 '18 at 11:04
1

I 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);
}
Community
  • 1
  • 1
ccerhan
  • 642
  • 6
  • 7
  • works for me! i replaced sleep with `usleep(10);` and removed the `printf("...\n");` – datahaki Jul 18 '17 at 11:15
  • 4
    Mixing `select()` on `STDIN_FILENO` and using `FILE`-based operations like `scanf()` on `stdin` is likely to cause problems. If, for example, `scanf()` reads and buffers a full 5 kb of input from `STDIN_FILENO`, but only copies a 30-byte line (to `message` in this example), there will still be almost 5,000 bytes in the `stdin` buffer that the sample program above will never see because there's nothing left at the file descriptor level. Don't do this. – Andrew Henle Jun 28 '18 at 10:48
  • Please don't use sleep; set `timeout.tv_sec` to 1 and `timeout.tv_usec` to 0 before calling `select`. This will still wait for one sec per loop when there's no input, but process your input immediately when there is any. – Guntram Blohm Jun 28 '19 at 16:05
0

There are many ways to check if stdin has input available. The most portable ones are, in that order: select, fcntl and 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 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:

DrBeco
  • 11,237
  • 9
  • 59
  • 76
  • 1
    Mixing `select()` on the file descriptor and reading using `stdin` via calls such as `fgets()` won't work to avoid blocking. Data can get read into a buffered `stdin` and remain there, leaving unread data that will never be detected by `select()` on the underlying file descriptor. Disabling buffering on `stdin` won't work either because then a call such as `fgets()` can block - and the only reason to check for input is to prevent such blocking. – Andrew Henle Jun 28 '18 at 10:56
  • Hi Andrew. Thanks for the input. Two things: `fgets()` is not the kernel of those snippets. New programmers have trouble even trying to call those functions. I could add that this problem will happen not only with `select()`. It is a fair warning to those using the examples. If you happen to know your data format, you can check and read them all (with `fgets()`, `scanf()`, `getc()`, `write()` or other). But if you have unformatted data coming in, you better check char by char. The examples I gave above will assume data ends with `\n` (except `termios()` that goes a char at time). – DrBeco Jun 29 '18 at 03:49
  • Second: tests I've made to fill `stdin` with redirection or file writing did work well using `select()`. On the other hand, `ioctl()` hangs if you fill `stdin` using redirection. Also, it is important to notice in those examples that `termios()` have a very different approach and the behavior is to indicate immediately as soon as a char is in the buffer. Those examples should be tamed by the programmer, they are not supposed to be a "solution to all your problems". Most important: know your data input, or be prepared for errors. – DrBeco Jun 29 '18 at 03:54
  • The logic will not work properly even with with a TTY device in character-at-a-time input mode if the user pastes a block of text into the terminal. The `stdin` stream will end up buffering multiple characters. `sevbuf(stdin, NULL);` fixes this on a glibc-based Linux system. I seem to recall that ISO C doesn't define buffering as having an effect on input streams though. – Kaz Nov 01 '18 at 13:57
  • POSIX does say that buffering is for input and output. But it doesn't spell out that an unbuffered input stream will issue one byte reads; it just says that the bytes "appear from the source as soon as possible". That does not rule out reading multiple bytes that are already available; in fact the one-byte-read is specifically delaying the passage of already-available input in the TTY into the buffer. – Kaz Nov 01 '18 at 14:04
0
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!='\0'){   //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 =='\0')

    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
    }