Cノンブロッキングキーボード入力


82

ユーザーがキーを押すまでループするプログラムをC(Linux上)で作成しようとしていますが、各ループを続行するためにキーを押す必要はありません。

これを行う簡単な方法はありますか?おそらくそれができると思いますがselect()、それは大変な作業のようです。

あるいは、非ブロッキングioの代わりに、プログラムが閉じる前にクリーンアップを行うためにctrl-cキーを押す方法はありますか?


スレッドを開くのはどうですか?
nadavB20年

回答:


65

すでに述べたように、sigactionctrl-cをトラップするため、またはselect標準入力をトラップするために使用できます。

ただし、後者の方法では、TTYを設定して、一度に1行ずつではなく一度に1文字ずつモードにする必要があることに注意してください。後者がデフォルトです。テキスト行を入力すると、Enterキーを押すまで実行中のプログラムのstdinに送信されません。

あなたは使用する必要があります tcsetattr()機能してICANONモードをオフにし、おそらくECHOも無効にするます。また、メモリから、プログラムの終了時に端末をICANONモードに戻す必要があります。

完全を期すために、Unix TTYをセットアップし、DOS<conio.h>関数kbhit()をエミュレートするコード(nb:エラーチェックなし!)をいくつか示しますgetch()


1
getch()は、押されたキーを返すことになっていますか、それとも単に文字を消費することになっていますか?
Salepate 2012

@Salepateは文字を返すことになっています。(void)そのキャラクターを取得し、それを破棄するのはビットです。
Alnitak 2012

1
なるほど、間違っているかもしれませんが、printf( "%c");でgetch()を使用すると 画面に奇妙な文字が表示されます。
Salepate 2012

少し編集します
。read

1文字ではなく行全体を読み取る簡単な方法はありますか(たとえば、「getline」を使用)?
azmeuk 2014

15

select()利便性のために少し低レベルです。ncursesライブラリを使用して端末をcbreakモードとdelayモードにしてから、を呼び出すことをお勧めしますgetch()。これERRは、準備ができている文字がない場合に返されます。

その時点で、getchブロックせずに呼び出すことができます。


cursesの使用も考えましたが、initscr()呼び出しによって画面がクリアされ、画面出力に通常のstdioを使用する際に邪魔になる可能性があります。
Alnitak

5
ええ、呪いはほとんどすべてか無かです。
ノーマンラムゼー

11

UNIXシステムでは、sigactioncallを使用してSIGINT、Control + Cキーシーケンスを表すシグナルのシグナルハンドラーを登録できます。シグナルハンドラは、ループでチェックされるフラグを設定して、適切にブレークさせることができます。


簡単にやることになると思いますが、タイトルの方が近いので、もう一方の答えも受け入れます。
zxaos 2009年

6

あなたはおそらく欲しい kbhit();

これは、すべての環境で機能するとは限りません。移植可能な方法は、監視スレッドを作成し、フラグを設定することです。getch();


4
間違いなくポータブルではありません。kbhit()は、DOS / WindowsコンソールのIO関数です。
Alnitak

1
それはまだ多くの用途で有効な答えです。OPは、移植性や特定のプラットフォームを指定していません。
AShelly 2009年

4
Alnitak:それがプラットフォームを意味していることに気づいていませんでした-同等のWindows用語は何ですか?
zxaos 2009年

3
@Alnitakプログラミングの概念として、なぜ「ノンブロッキング」がUnix環境でより馴染みがあるのでしょうか。
MestreLion 2015

4
@Alnitak:* nixに触れるずっと前から、「ノンブロッキングコール」という用語に精通していたので、Zxaosと同じように、プラットフォームを意味することに気付くことはありませんでした。
MestreLion 2015

4

ノンブロッキングキーボード入力を取得する別の方法は、デバイスファイルを開いて読み取ることです。

/ dev / input / event *のいずれかで、探しているデバイスファイルを知っている必要があります。cat / proc / bus / input / devicesを実行して、必要なデバイスを見つけることができます。

このコードは私のために機能します(管理者として実行します)。


素晴らしい例ですが、6つ以上のキーが保持されたときにイベントデバイスが発行を停止したときに、Xorgがキーイベントを受信し続けるという問題に遭遇しました:\奇妙なことですか?
ThorSummoner

3

cursesライブラリはこの目的に使用できます。もちろん、select()シグナルハンドラーもある程度使用できます。


1
cursesはライブラリの名前であり、使用するときにつぶやくものでもあります;)ただし、私が言及するつもりだったので、+ 1します。
Wayne Werner

2

Control-Cをキャッチするだけで満足しているなら、それは完了した取引です。本当にノンブロッキングI / Oが必要であるが、cursesライブラリは必要ない場合、別の方法は、ロック、ストック、バレルをAT&Tsfioライブラリに移動することです。これはCでパターン化された優れたライブラリですstdioが、より柔軟性があり、スレッドセーフであり、パフォーマンスが向上します。(sfioは安全で高速なI / Oの略です。)


1

これを行うための移植可能な方法はありませんが、select()が良い方法かもしれません。その他の可能な解決策については、http://c-faq.com/osdep/readavail.htmlを参照してください


1
この答えは不完全です。tcsetattr()による端末操作なし[私の答えを参照] Enterキーを押すまで、fd0では何も取得されません。
Alnitak

0

次のようにselectを使用してこれを行うことができます。

あまり仕事はありません:D


2
この答えは不完全です。端末を非正規モードに設定する必要があります。またnfds、1に設定する必要があります
。– luser droog 2016

0

これを行うための関数は次のとおりです。termios.hPOSIXシステムに付属しているものが必要です。

これを分解する:tcgetattr現在の端末情報を取得し、に保存しtます。cmdが1の場合、のローカル入力フラグtは非ブロッキング入力に設定されます。それ以外の場合はリセットされます。次に、tcsetattr標準入力をに変更しtます。

プログラムの最後に標準入力をリセットしないと、シェルで問題が発生します。


switchステートメントに
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.