2011-11-11 13 views
4

私は、ユーザーが文字を入力しない限り、ファイルが終了するまでファイルをゆっくり(1秒間隔で)印刷する必要がある練習をしています。stdinノンブロッキングの作成

これまでのところ、プログラムは1秒間隔でファイルを出力しますが、文字を入力すると何も起こりません。私の推測では、何とか間違った選択をしています。

これは私が提出した最後のプログラムです。

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

int main(void) 
{ 
    FILE* infile; 
    char str[100]; 
    fd_set readset; 
    struct timeval tv; 

    // open a file 
    if((infile = fopen("infile", "r")) == NULL) 
    { 
     (void)printf("Couldn't open the file\n"); 
     exit(1); 
    } 
    // file was opened successfully 
    else 
    {  
     // while we are not at the end of a file 
     while(fgets(str, 100, infile) != NULL) 
     { 
      FD_ZERO(&readset); 
      FD_SET(fileno(stdin), &readset); 
      // set the time value to 1 second 
      tv.tv_sec = 1; 
      tv.tv_usec = 0; 
      select(fileno(infile)+1, &readset, NULL, NULL, &tv); 
      // the user typed a character so exit 
      if(FD_ISSET(fileno(stdin), &readset)) 
      { 
       fclose(infile); 
       exit(0); 
      } 
      // the user didn't type a character so print the next line 
      else 
      { 
       fgets(str, 100, stdin); 
       puts(str); 
      } 
     } 

     // clean up 
     fclose(infile); 
    } 

    // report success 
    return 0; 
} 

ありがとうございました!

+0

なぜ 'writeet'に' infile'を置いていますか?あなたはそれに書いていません。 – aschepler

+0

良い点、これを反映するように更新します。 –

+0

推測すると、 'infile'が読み取り専用で開かれたことを考えると' writeset'に 'infile'を持つのは良くないでしょう。しかし、これが問題を引き起こしているかどうかはわかりません。 –

答えて

6

これは、tcgetattr/tcsetattr:

#include <stdio.h> 
#include <stdlib.h> 
#include <sys/select.h> 
#include <sys/time.h> 
#include <sys/types.h> 
#include <unistd.h> 
#include <fcntl.h> 
#include <termios.h> 

int main(void) { 
    FILE* infile; 
    char str[100]; 
    fd_set readset; 
    struct timeval tv; 
    struct termios ttystate, ttysave; 

    // open a file 
    if((infile = fopen("infile", "r")) == NULL) 
    { 
     (void)printf("Couldn't open the file\n"); 
     exit(1); 
    } 
    // file was opened successfully 

    //get the terminal state 
    tcgetattr(STDIN_FILENO, &ttystate); 
    ttysave = ttystate; 
    //turn off canonical mode and echo 
    ttystate.c_lflag &= ~(ICANON | ECHO); 
    //minimum of number input read. 
    ttystate.c_cc[VMIN] = 1; 

    //set the terminal attributes. 
    tcsetattr(STDIN_FILENO, TCSANOW, &ttystate); 

    // while we are not at the end of a file 
    while(fgets (str, 100, infile)) 
    { 
     // set the time value to 1 second 
     tv.tv_sec = 1; 
     tv.tv_usec = 0; 

     FD_ZERO(&readset); 
     FD_SET(fileno(stdin), &readset); 

     select(fileno(stdin)+1, &readset, NULL, NULL, &tv); 
     // the user typed a character so exit 
     if(FD_ISSET(fileno(stdin), &readset)) 
     { 
      fgetc (stdin); // discard character 
      break; 
     } 
     // the user didn't type a character so print the next line 
     else 
     { 
      puts(str); 
      // not needed: sleep(1); 
     } 
    } 

    // clean up 
    fclose(infile); 

    ttystate.c_lflag |= ICANON | ECHO; 
    //set the terminal attributes. 
    tcsetattr(STDIN_FILENO, TCSANOW, &ttysave); 
    // report success 
    return 0; 
} 

sleep(1);はもう必要ありません。

+0

プログラムはまだ私のために同じ実行されます。しかし、入力をありがとう。 –

+0

@MJPiarulli:私は自分の答えを編集しました。 –

+0

このプログラムは、私のプログラムの実行方法を正確に実行します。あなたの答えをありがとうございました。残念ながら、私たちはtermiosヘッダーについては一度も話したことがありませんので、私の教授は別の答えを思いついたと思います。あなたの努力のためにもう一度ありがとう。 –

0

プログラムをマルチスレッドにしたいと思うでしょう。ファイルを1秒間隔で出力するスレッドを作成し、メインスレッドがstdinから入力を受け取り、入力を取得するたびに印刷を停止するように他のスレッドに通知します。

+0

提案していただきありがとうございますが、まだクラスに複数のスレッドを作成することについては学んでいないので、私の教授は、私たちはこの別の方法について行きます。 –

3

端末がバッファリングしています。 キーを押すまで、プログラムにテキストを送信しません。端末回線のバッファリングを無効にする方法があるかもしれませんが、私はそれがあなたの割り当ての範囲を超えていると思います。

を入力すると停止します。と入力します。ただし、すぐに終了しません。それは修正したいものです。そのうちsleep(1)を取り除く。

あなたのプログラムはテキストをスパムします。あなたはselectに1秒のタイムアウトを与えましたか?

// set the time value to 1 second 
tv.tv_sec = 1; 
tv.tv_usec = 0; 

selectは、タイムアウト値を変更しているので、タイムアウトは固執しない理由があります。 the man pageから:Linuxの

、選択()ではない が寝た時間の量を反映するために、タイムアウトを修正。他のほとんどの実装はこれをしません。これは、タイムアウトを読み取るLinuxコード が他のオペレーティングシステムに移植されたときと、 select()のstruct timevalを再利用するコード がLinuxに移植されたときの両方で問題を引き起こしています(POSIX.1-2001 )ループを再初期化せずにループします。 select()が復帰した後には、タイムアウトは になります。

あなたは、プログラムの先頭で一度だけではなく、選択するtimeval前にすべてのコールを初期化する必要があります。

+0

私のキャラクターが改行に先行するように端末がバッファリングしていることに気がつきました。しかし、私はselect()がLinux上で異なって実装されたことを認識しませんでした。私は変更を提案して、プログラムはまだ同じ実行されます。文字を入力してEnterキーを押しても、端末は切断して、:コマンドが見つからないと伝えています。 –

+1

@MJPiarulli:あなたの入力がプログラムによって消費されない場合(例えば、 'read'や' fgets'を使って)、それは代わりに端末によって読み込まれます。 –

0

問題の一部は、sleep(1)を使用しているため、その行の実行に1秒かかることがあります。ユーザーが文字を入力すると、プログラムが応答するまでに最大1秒間待たなければなりません。したがって、一度ノンブロッキング部分が動作しても、それでも問題は発生します。

解決策は、nanosleepまたはusleepを使用して1秒未満の間プログラムを一時停止することです。これらの機能の1つを使って1/100秒間スリープ状態にすることをお勧めします)、毎回ユーザーのキーが押されているかどうかを確認してください。 100回目に、ファイルの次の部分を出力します。そうすれば、ファイルはまだ適切な速度になりますが、ユーザーはいつでも停止することができ、プログラムはコマンドに非常に迅速に応答します。

+2

'select'を使うと、繰り返しキーを押さなくても確認できます。 'select'はスリープして同時に入力を待つことができます。 –