web-dev-qa-db-ja.com

scanf()が入力文字を永​​久に待つのを防ぐにはどうすればよいですか?

コンソールアプリケーションで次のことを実行したいと思います。

  1. ユーザーが文字を入力すると、アプリケーションは対応するタスクを実行します。たとえば、ユーザーが_1_を入力すると、プログラムはタスク1を実行し、ユーザーがqを入力すると、プログラムは終了します。
  2. ユーザーが何も入力しない場合、プログラムは10秒ごとにデフォルトのタスクを実行します(時間は非常に厳密である必要はありません)。

これが私のコードです:

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

}
_

ただし、問題は、文字が入力されていない場合、プログラムがscanf()関数の行を永久にトラップして入力文字を待機することです。したがって、一定時間後にscanf()の行をスキップする方法を考えています。たとえば、1秒後に入力がない場合、プログラムは上記の2番目のことを実行するために続行できます。

重要な場合、プラットフォームはWindowsです。

while()の後にセミコロンを削除しました。これは明らかな間違いでした。

14
user3208536

Select()関数を使用してみてください。次に、ブロックせずにstdinから読み取ることができるようになるまで10秒間待つことができます。 select()がゼロで戻る場合は、デフォルトのアクションを実行します。これがWindowsで機能するかどうかはわかりませんが、POSIX標準です。 UNIX/Linuxで開発する場合は、man selectを試してください。

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

注:以下のpoll()の例も問題ありません。残りの部分については、使用可能なバイトをバッファー(MAXBYTESまで)に読み込むことを選択しました。後でスキャンすることができます。 (scanf()は私の友達ではありませんが、それは個人的な好みの問題です)。

12
Ronald

poll(おそらく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;
}

タイムアウトはpollの3番目の引数であり、ミリ秒単位です。この例では、stdinへの入力を2秒間待機します。 WindowsにはWSAPollがあり、これも同様に機能するはずです。

9
Graeme

しかし、問題は、プログラムがscanf()関数の行を永久にトラップして、入力文字を待機することです。

whileの後のセミコロンを削除します。

3
haccks

アラームを試す(3)

#include <stdio.h>
#include <unistd.h>
int main(void)
{
   char buf [10];
   alarm(3);
   scanf("%s", buf);
   return 0;
}
2
lbaby

他の人が言っているように、真に非同期を行う最良の方法IOは、select(...)を使用することです。

しかし、あなたが望むことをするための迅速で汚い方法は、getline(...)を使用することです。これは、毎回読み取られるバイト数を返し(IOにハングアップしない)、読み取られないバイト数で-1を返します。

以下は、getline(3)のマニュアルページからのものです。

// 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);
2
mellowfish