ユーザーがキーを押すまでループするプログラムをC(Linux上)で作成しようとしていますが、各ループを続行するためにキーを押す必要はありません。
これを行う簡単な方法はありますか?おそらくそれができると思いますがselect()
、それは大変な作業のようです。
あるいは、非ブロッキングioの代わりに、プログラムが閉じる前にクリーンアップを行うためにctrl-cキーを押す方法はありますか?
回答:
すでに述べたように、sigaction
ctrl-cをトラップするため、またはselect
標準入力をトラップするために使用できます。
ただし、後者の方法では、TTYを設定して、一度に1行ずつではなく一度に1文字ずつモードにする必要があることに注意してください。後者がデフォルトです。テキスト行を入力すると、Enterキーを押すまで実行中のプログラムのstdinに送信されません。
あなたは使用する必要があります tcsetattr()
機能してICANONモードをオフにし、おそらくECHOも無効にするます。また、メモリから、プログラムの終了時に端末をICANONモードに戻す必要があります。
完全を期すために、Unix TTYをセットアップし、DOS<conio.h>
関数kbhit()
をエミュレートするコード(nb:エラーチェックなし!)をいくつか示します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);
}
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 */
}
(void)
そのキャラクターを取得し、それを破棄するのはビットです。
select()
利便性のために少し低レベルです。ncurses
ライブラリを使用して端末をcbreakモードとdelayモードにしてから、を呼び出すことをお勧めしますgetch()
。これERR
は、準備ができている文字がない場合に返されます。
WINDOW *w = initscr();
cbreak();
nodelay(w, TRUE);
その時点で、getch
ブロックせずに呼び出すことができます。
UNIXシステムでは、sigaction
callを使用してSIGINT
、Control + Cキーシーケンスを表すシグナルのシグナルハンドラーを登録できます。シグナルハンドラは、ループでチェックされるフラグを設定して、適切にブレークさせることができます。
あなたはおそらく欲しい 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();
ノンブロッキングキーボード入力を取得する別の方法は、デバイスファイルを開いて読み取ることです。
/ dev / input / event *のいずれかで、探しているデバイスファイルを知っている必要があります。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ライブラリは必要ない場合、別の方法は、ロック、ストック、バレルをAT&Tsfio
ライブラリに移動することです。これはCでパターン化された優れたライブラリですstdio
が、より柔軟性があり、スレッドセーフであり、パフォーマンスが向上します。(sfioは安全で高速なI / Oの略です。)
これを行うための移植可能な方法はありませんが、select()が良い方法かもしれません。その他の可能な解決策については、http://c-faq.com/osdep/readavail.htmlを参照してください。
次のように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
nfds
、1に設定する必要があります
これを行うための関数は次のとおりです。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
ます。cmd
が1の場合、のローカル入力フラグt
は非ブロッキング入力に設定されます。それ以外の場合はリセットされます。次に、tcsetattr
標準入力をに変更しt
ます。
プログラムの最後に標準入力をリセットしないと、シェルで問題が発生します。