ユーザーがキーを押すまでループするプログラムをC(Linux上)で作成しようとしていますが、各ループを続行するためにキーを押す必要はありません。
これを行う簡単な方法はありますか?おそらくそれでできると思いますがselect()
、それは大変な作業のようです。
または、ノンブロッキング io の代わりにプログラムが終了する前にctrl-キーを押してクリーンアップを行う方法はありますか?c
ユーザーがキーを押すまでループするプログラムをC(Linux上)で作成しようとしていますが、各ループを続行するためにキーを押す必要はありません。
これを行う簡単な方法はありますか?おそらくそれでできると思いますがselect()
、それは大変な作業のようです。
または、ノンブロッキング io の代わりにプログラムが終了する前にctrl-キーを押してクリーンアップを行う方法はありますか?c
すでに述べたように、 を使用sigaction
して ctrl-cselect
をトラップしたり、標準入力をトラップしたりできます。
ただし、後者の方法では、一度に行単位モードではなく、一度に文字単位モードになるように TTY を設定する必要があることに注意してください。後者がデフォルトです。テキスト行を入力すると、Enter キーを押すまで実行中のプログラムの stdin に送信されません。
関数を使用tcsetattr()
してICANONモードをオフにし、おそらくECHOも無効にする必要があります。また、メモリから、プログラムの終了時に端末を ICANON モードに戻す必要があります。
<conio.h>
完全を期すために、Unix TTY を設定し、DOS機能kbhit()
をエミュレートするコードを作成しました (注意: エラー チェックはありません!) getch()
。
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <termios.h>
struct termios orig_termios;
void reset_terminal_mode()
{
tcsetattr(0, TCSANOW, &orig_termios);
}
void set_conio_terminal_mode()
{
struct termios new_termios;
/* take two copies - one for now, one for later */
tcgetattr(0, &orig_termios);
memcpy(&new_termios, &orig_termios, sizeof(new_termios));
/* register cleanup handler, and set the new terminal mode */
atexit(reset_terminal_mode);
cfmakeraw(&new_termios);
tcsetattr(0, TCSANOW, &new_termios);
}
int kbhit()
{
struct timeval tv = { 0L, 0L };
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
return select(1, &fds, NULL, NULL, &tv) > 0;
}
int getch()
{
int r;
unsigned char c;
if ((r = read(0, &c, sizeof(c))) < 0) {
return r;
} else {
return c;
}
}
int main(int argc, char *argv[])
{
set_conio_terminal_mode();
while (!kbhit()) {
/* do some work */
}
(void)getch(); /* consume the character */
}
select()
便宜上、レベルが低すぎます。ncurses
ライブラリを使用して端末を cbreak モードと遅延モードにしてから、 を呼び出すことをお勧めします。これは、準備ができている文字がない場合getch()
に返されます。ERR
WINDOW *w = initscr();
cbreak();
nodelay(w, TRUE);
その時点で、getch
ブロックせずに呼び出すことができます。
UNIX システムでは、call を使用して、Control+C キー シーケンスを表すシグナルsigaction
のシグナル ハンドラを登録できます。SIGINT
シグナルハンドラーは、ループでチェックされるフラグを設定して、適切にブレークすることができます。
あなたはおそらくしたいですkbhit();
//Example will loop until a key is pressed
#include <conio.h>
#include <iostream>
using namespace std;
int main()
{
while(1)
{
if(kbhit())
{
break;
}
}
}
これはすべての環境で機能するとは限りません。移植可能な方法は、監視スレッドを作成し、いくつかのフラグを設定することですgetch();
ノンブロッキング キーボード入力を取得するもう 1 つの方法は、デバイス ファイルを開いて読み取ることです。
探しているデバイス ファイル (/dev/input/event* の 1 つ) を知っている必要があります。cat /proc/bus/input/devices を実行して、必要なデバイスを見つけることができます。
このコードは私にとってはうまくいきます(管理者として実行します)。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/input.h>
int main(int argc, char** argv)
{
int fd, bytes;
struct input_event data;
const char *pDevice = "/dev/input/event2";
// Open Keyboard
fd = open(pDevice, O_RDONLY | O_NONBLOCK);
if(fd == -1)
{
printf("ERROR Opening %s\n", pDevice);
return -1;
}
while(1)
{
// Read Keyboard Data
bytes = read(fd, &data, sizeof(data));
if(bytes > 0)
{
printf("Keypress value=%x, type=%x, code=%x\n", data.value, data.type, data.code);
}
else
{
// Nothing read
sleep(1);
}
}
return 0;
}
curses ライブラリは、この目的に使用できます。もちろん、select()
シグナルハンドラもある程度使用できます。
Control-C をキャッチするだけで満足している場合は、それで完了です。ノンブロッキング I/O が本当に必要だが、curses ライブラリは必要ない場合は、lock、stock、barrel をAT&Tsfio
ライブラリに移動するという別の方法があります。C でパターン化された優れたライブラリですstdio
が、より柔軟で、スレッドセーフで、パフォーマンスが向上しています。(sfio は、安全で高速な I/O の略です。)
次のように select を使用してそれを行うことができます。
int nfds = 0;
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(0, &readfds); /* set the stdin in the set of file descriptors to be selected */
while(1)
{
/* Do what you want */
int count = select(nfds, &readfds, NULL, NULL, NULL);
if (count > 0) {
if (FD_ISSET(0, &readfds)) {
/* If a character was pressed then we get it and exit */
getchar();
break;
}
}
}
あまり仕事ではありません:D
これを行う移植可能な方法はありませんが、select() は良い方法かもしれません。可能な解決策については、 http://c-faq.com/osdep/readavail.htmlを参照してください。
これを行う関数を次に示します。termios.h
POSIX システムに付属するものが必要です。
#include <termios.h>
void stdin_set(int cmd)
{
struct termios t;
tcgetattr(1,&t);
switch (cmd) {
case 1:
t.c_lflag &= ~ICANON;
break;
default:
t.c_lflag |= ICANON;
break;
}
tcsetattr(1,0,&t);
}
これを分析するtcgetattr
と、現在の端末情報を取得して に保存しt
ます。が 1 の場合cmd
、ローカル入力フラグ int
は非ブロッキング入力に設定されます。それ以外の場合はリセットされます。次に、tcsetattr
標準入力を に変更しt
ます。
プログラムの最後で標準入力をリセットしないと、シェルで問題が発生します。