0

端末 I/O の仕組みを理解しようとしています。

端末が次のように非正規モードに設定されている場合 (エラー処理がありません):

struct termios term_original, term_current;
tcgetattr(STDIN_FILENO, &term_original);
term_current = term_original;
term_current.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO);
term_current.c_iflag &= ~(BRKINT | ICRNL | IGNBRK | IGNCR | INLCR | INPCK | ISTRIP | IXON | PARMRK);
term_current.c_oflag &= ~(OPOST);
term_current.c_cc[VMIN]  = 1;
term_current.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSADRAIN, &term_current);

単純な読み取りループは、ボタンを押すたびに生成されたデータを次のように読み取ることができます。

char c;
while (read(0, &c, 1) != -1) { PRINT_CHAR(c); }

今、

  • キーボードで Esc を押すと、0x1b が生成されます。
  • F1 を押すと、0x1b 0x4f 0x50 が生成されます。
  • F5 を押すと、0x1b 0x5b 0x31 0x35 0x7e が生成されます。

この入力を読み取って処理するという点では、あるボタンを押したときの出力がどこで終わり、次のボタンが始まるかをどのように判断するのでしょうか? 私は識別可能なパターンを見つけることができませんでした.Escが単一バイトを生成するという事実は、ほとんどのマルチバイト生成ボタン押下の出力の最初のバイトと同じであることを示唆しているようです. ボタンの境界がどこにあるかを判断するための他のメカニズムはありますか?

4

3 に答える 3

2

プログラムは、キーがあまり速く押されないことに依存しています。遅延が 100 ミリ秒未満の場合、これは 1 つのキーを押したことになります。それ以外の場合は、2 つの別個のイベントがあります。

はい、プログラムは実際にはESCが押された後しばらく一時停止します。これは、ESCであり、他のキーがないことを確認するためです。この一時停止は、肉眼で識別できる場合があります。

一部のプログラムは、このタイミングを微調整するために ESCDELAY 環境変数を認識します。

はい、これは完璧ではありません。キーを速く押すと、システムをだますことができます。

于 2013-05-13T03:42:58.317 に答える
1

さて、nm のおかげで、ここで正しい軌道に乗ることができました。

一度に 1 バイトずつ読み取ろうとするのは正しくありません。むしろ、一度に複数の文字を読み取ろうとするべきです。

次のようなもの:

int r, i;
char buffer[10]; //10 chosen arbitrarily
while ((r = read(STDIN_FILENO, buffer, sizeof(buffer))) != -1)
{
  printf("%d bytes: ", r);
  for (i = 0; i < r; ++i) { PRINT_CHAR(buffer[i]); }
  printf("\r\n");
}

この場合、ボタンが押されるとすぐに read() 呼び出しが返され、読み取られたバイト数が返されます。これで、バイトを使用して問題のボタンまたは文字を識別できます。

上記のループを使用して一番上の行のボタンを押すと、次のように表示されます。

1 bytes: 1b
3 bytes: 1b 4f 50
3 bytes: 1b 4f 51
3 bytes: 1b 4f 52
3 bytes: 1b 4f 53
5 bytes: 1b 5b 31 35 7e
5 bytes: 1b 5b 31 37 7e

私のマシンでは、次のようになっているようです:

  • ASCII 文字の 1 バイト。
  • 最初の文字は 0x1b で、その後に特別なボタン用の他の文字 (F1-F12、上、下など...) が続きます。
  • 問題の文字の UTF-8 表現であることが判明した、非 ASCII 文字のその他のマルチバイト シーケンス。

私は狂ったようにキーボードのボタンを押し込んでみましたが、上記のループは常に、どのバイトが単一の単位であるかを正しく識別できました。

ただし、負荷の高いマシンや、バッファリングされた遅延の大きいネットワーク接続では、これは完全には機能しない可能性があります。おそらく、そのような状況では、後で複数のボタンを押してからのバイト数が端末バッファーにすでにあるため、複数のボタンが 1 つとして表示されます。

このような状況では、おそらくエラーが発生しないことを保証する方法はありませんが、エラーを最小限に抑えることはできます。1 バイト文字は常に 0x00 ~ 0x7F の範囲にあるように見えます。特殊ボタンは常にマルチバイトで、0x1B で始まり、その後に 0x00 ~ 0x7F 内の何かが続きます。マルチバイト文字は常に 0x80 ~ 0xFF の範囲です。UTF-8 エンコード シーケンスには、現在の文字のバイト数を示す最初のバイトもあります。この情報があれば、エラーを最小限に抑え、今後の読み取りに不必要に伝播しないようにするのに十分です。

最後に、私が説明したことは私のマシン (PC、クラシック US 101 キーボード、UTF-8 に設定された端末エンコーディング) に関するものであることを強調することが重要です。完全なプログラムは、端末が使用している文字エンコーディングを最小限に抑える必要があります。

于 2013-05-13T14:21:57.687 に答える