Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I prevent scanf() to wait forever for an input character?

Tags:

c

scanf

I want to fulfill the following things in a console application:

  1. If user inputs a character, the application will do the corresponding task. For example, if user inputs 1, the program will do task 1, if user inputs q, the program will quit;
  2. If user inputs nothing, the program will do the default task every 10 seconds (the time needn't to be very strict).

Here is my code:

#include <stdio.h>
#include <time.h>

char buff[64];
char command;

while(command != 'q')
{
   begin:
   printf(">> ");
   scanf("%s", buff);
   command = buff[0];

   switch (command)
   {
       case '1':
       // task 1 code will be added here;
          break;
       case '2':
       // task 2 code will be added here;
          break;
       case 'q':
          printf("quit the loop.\n");
          break;
   }

   // wait for 10 seconds;
   Sleep(10000);
   // default task code will be added here;

  if(command != 'q')
  {
     goto begin;
  }

}

But the problem is the program will trap at the line of scanf() function forever to wait for an input character, if no character is entered. So I'm wondering how to skip the line of scanf() after a certain time, I mean for example, if no input after 1 second, the program can continue, so as to fulfill the second thing listed above.

The platform is Windows, if it matters.

I've removed the semicolon after the while() it was an obvious mistake.

like image 402
user3208536 Avatar asked Jan 17 '14 23:01

user3208536


5 Answers

Try using the select() function. Then you can wait for 10 seconds until you can read from stdin without blocking. If select() returns with zero, perform the default action. I don't know if this works on windows, it's POSIX standard. If you happen to develop on unix/linux, try man select

I just wrote a working example using select:

#include <stdlib.h>
#include <stdio.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#define MAXBYTES 80

int main(int argc, char *argv[])
{
        fd_set readfds;
        int    num_readable;
        struct timeval tv;
        int    num_bytes;
        char   buf[MAXBYTES];
        int    fd_stdin;

        fd_stdin = fileno(stdin);

        while(1) {
                FD_ZERO(&readfds);
                FD_SET(fileno(stdin), &readfds);

                tv.tv_sec = 10;
                tv.tv_usec = 0;

                printf("Enter command: ");
                fflush(stdout);
                num_readable = select(fd_stdin + 1, &readfds, NULL, NULL, &tv);
                if (num_readable == -1) {
                        fprintf(stderr, "\nError in select : %s\n", strerror(errno));
                        exit(1);
                }
                if (num_readable == 0) {
                        printf("\nPerforming default action after 10 seconds\n");
                        break;  /* since I don't want to test forever */
                } else {
                        num_bytes = read(fd_stdin, buf, MAXBYTES);
                        if (num_bytes < 0) {
                                fprintf(stderr, "\nError on read : %s\n", strerror(errno));
                                exit(1);
                        }
                        /* process command, maybe by sscanf */
                        printf("\nRead %d bytes\n", num_bytes);
                        break; /* to terminate loop, since I don't process anything */
                }
        }

        return 0;
}

Note: the poll() example below is OK too, no problem. For the rest I chose to read the available bytes into a buffer (up to MAXBYTES). It can be (s)scanned afterwards. (scanf() just isn't too much my friend, but that's a personal taste matter).

like image 73
Ronald Avatar answered Nov 10 '22 13:11

Ronald


Here is a working example of how to do this with poll (probably the most 'correct' way on Linux):

#include <unistd.h>
#include <poll.h>
#include <stdio.h>

int main()
{
    struct pollfd mypoll = { STDIN_FILENO, POLLIN|POLLPRI };
    char string[10];

    if( poll(&mypoll, 1, 2000) )
    {
        scanf("%9s", string);
        printf("Read string - %s\n", string);
    }
    else
    {
        puts("Read nothing");
    }

    return 0;
}

The timeout is the third argument to poll and is in milliseconds - this example will wait for 2 seconds for input on stdin. Windows has WSAPoll, which should work similarly.

like image 9
Graeme Avatar answered Nov 10 '22 15:11

Graeme


But the problem is the program will trap at the line of scanf() function forever to wait for an input character,

Remove the semicolon after while.

like image 3
haccks Avatar answered Nov 10 '22 14:11

haccks


Try alarm(3)

#include <stdio.h>
#include <unistd.h>
int main(void)
{
   char buf [10];
   alarm(3);
   scanf("%s", buf);
   return 0;
}
like image 2
lbaby Avatar answered Nov 10 '22 14:11

lbaby


As others have said, the best way to do truly async IO is with select(...).

But a quick and dirty way to do what you want is with getline(...) which will return the number of bytes read every time (not hanging on IO) and returns -1 on no bytes read.

The following is from the getline(3) man page:

// The following code fragment reads lines from a file and writes them to standard output.  
// The fwrite() function is used in case the line contains embedded NUL characters.

char *line = NULL;
size_t linecap = 0;
ssize_t linelen;
while ((linelen = getline(&line, &linecap, fp)) > 0)
    fwrite(line, linelen, 1, stdout);
like image 2
mellowfish Avatar answered Nov 10 '22 15:11

mellowfish