web-dev-qa-db-ja.com

Enterキーが押されるのを待たずに、標準入力から文字をキャプチャします

これは私にはめったに起こらないので、私がこれをどのように行ったかは決して覚えていません。しかし、CまたはC++では、改行を待たずに標準入力から文字を読み取る最良の方法は何ですか(Enterキーを押します)。

また、理想的には、入力文字を画面にエコーしません。コンソール画面に影響を与えずにキーストロークをキャプチャしたいだけです。

158
Adam

純粋なC++では、stdin(通常はラインバッファリング)で接続されている可能性のある端末に大きく依存するため、移植可能な方法では不可能です。ただし、そのためにライブラリを使用できます。

  1. conioはWindowsコンパイラで利用可能です。 Enterキーを待たずに文字を与えるには、_getch()関数を使用します。私は頻繁にWindows開発者ではありませんが、クラスメートが<conio.h>を含めて使用するのを見てきました。ウィキペディアの conio.h を参照してください。 Visual C++で非推奨と宣言されているgetch()をリストします。

  2. linuxで利用可能なcurses。互換性のあるcurses実装は、Windowsでも利用できます。また、getch()関数もあります。 (マンページを表示するには、man getchを試してください)。ウィキペディアの Curses を参照してください。

プラットフォーム間の互換性を目指す場合は、cursesを使用することをお勧めします。そうは言っても、ラインバッファリングをオフにするために使用できる関数があると確信しています(「クックモード」ではなく「ローモード」と呼ばれると思います-man sttyを調べてください)。私が間違っていなければ、呪いはあなたのためにポータブルな方法でそれを処理します。

Linux(および他のUnix系システム)では、これは次の方法で実行できます。

#include <unistd.h>
#include <termios.h>

char getch() {
        char buf = 0;
        struct termios old = {0};
        if (tcgetattr(0, &old) < 0)
                perror("tcsetattr()");
        old.c_lflag &= ~ICANON;
        old.c_lflag &= ~ECHO;
        old.c_cc[VMIN] = 1;
        old.c_cc[VTIME] = 0;
        if (tcsetattr(0, TCSANOW, &old) < 0)
                perror("tcsetattr ICANON");
        if (read(0, &buf, 1) < 0)
                perror ("read()");
        old.c_lflag |= ICANON;
        old.c_lflag |= ECHO;
        if (tcsetattr(0, TCSADRAIN, &old) < 0)
                perror ("tcsetattr ~ICANON");
        return (buf);
}

基本的に、標準モード(およびエコーを抑制するにはエコーモード)をオフにする必要があります。

76
anon

同じ問題を解決しようとしているときに、別のフォーラムでこれを見つけました。私が見つけたものから少し変更しました。それは素晴らしい作品です。私はOS Xを実行しているため、Microsoftを実行している場合は、rawモードおよびcookedモードに切り替えるための正しいsystem()コマンドを見つける必要があります。

#include <iostream> 
#include <stdio.h>  
using namespace std;  

int main() { 
  // Output Prompt 
  cout << "Press any key to continue..." << endl; 

  // Set terminal to raw mode 
  system("stty raw"); 

  // Wait for single character 
  char input = getchar(); 

  // Echo input:
  cout << "--" << input << "--";

  // Reset terminal to normal "cooked" mode 
  system("stty cooked"); 

  // And we're out of here 
  return 0; 
}
16
cwhiii

CONIO.H

必要な機能は次のとおりです。

int getch();
Prototype
    int _getch(void); 
Description
    _getch obtains a character  from stdin. Input is unbuffered, and this
    routine  will  return as  soon as  a character is  available  without 
    waiting for a carriage return. The character is not echoed to stdout.
    _getch bypasses the normal buffering done by getchar and getc. ungetc 
    cannot be used with _getch. 
Synonym
    Function: getch 


int kbhit();
Description
    Checks if a keyboard key has been pressed but not yet read. 
Return Value
    Returns a non-zero value if a key was pressed. Otherwise, returns 0.

libconio http://sourceforge.net/projects/libconio

または

Conio.hのLinux c ++実装 http://sourceforge.net/projects/linux-conioh

14
dddomodossola
#include <conio.h>

if (kbhit() != 0) {
    cout << getch() << endl;
}

これは、kbhit()を使用してキーボードが押されているかどうかを確認し、getch()を使用して押されている文字を取得します。

9
Joseph Dykstra

Windowsを使用している場合は、 PeekConsoleInput を使用して入力があるかどうかを検出できます。

HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
DWORD events;
INPUT_RECORD buffer;
PeekConsoleInput( handle, &buffer, 1, &events );

次に、ReadConsoleInputを使用して入力文字を「消費」します。

PeekConsoleInput(handle, &buffer, 1, &events);
if(events > 0)
{
    ReadConsoleInput(handle, &buffer, 1, &events);  
    return buffer.Event.KeyEvent.wVirtualKeyCode;
}
else return 0

正直に言うと、これは私が持っている古いコードからのものであるため、少し手を加えなければなりません。

ただし、クールなのは、何も入力せずに入力を読み取るため、文字がまったく表示されないことです。

8
hasen

Windowsを想定して、ReadConsoleInput関数を見てください。

5
Tritium

CとC++はI/Oの非常に抽象的な視点を取り、あなたが望むことをする標準的な方法はありません。取得するものがあれば、標準入力ストリームから文字を取得する標準的な方法があり、他の言語ではどちらも定義されていません。したがって、おそらくオペレーティングシステムだけでなく、ソフトウェアフレームワークにも依存する、プラットフォーム固有の回答が必要になります。

ここには合理的な推測がいくつかありますが、ターゲット環境が何であるかを知らずに質問に答える方法はありません。

5
David Thornley

ポータブルに最も近いのは、 ncurses ライブラリを使用して端末を「cbreakモード」にすることです。 APIは巨大です。最も必要なルーチンは

  • initscrおよびendwin
  • cbreakおよびnocbreak
  • getch

幸運を!

4
Norman Ramsey

Kbhit()を使用してcharが存在するかどうかを確認し、getchar()を使用してデータを読み取ります。 Windowsでは、「conio.h」を使用できます。 Linuxでは、独自のkbhit()を実装する必要があります。

以下のコードを参照してください:

// kbhit
#include <stdio.h>
#include <sys/ioctl.h> // For FIONREAD
#include <termios.h>
#include <stdbool.h>

int kbhit(void) {
    static bool initflag = false;
    static const int STDIN = 0;

    if (!initflag) {
        // Use termios to turn off line buffering
        struct termios term;
        tcgetattr(STDIN, &term);
        term.c_lflag &= ~ICANON;
        tcsetattr(STDIN, TCSANOW, &term);
        setbuf(stdin, NULL);
        initflag = true;
    }

    int nbbytes;
    ioctl(STDIN, FIONREAD, &nbbytes);  // 0 is STDIN
    return nbbytes;
}

// main
#include <unistd.h>

int main(int argc, char** argv) {
    char c;
    //setbuf(stdout, NULL); // Optional: No buffering.
    //setbuf(stdin, NULL);  // Optional: No buffering.
    printf("Press key");
    while (!kbhit()) {
        printf(".");
        fflush(stdout);
        sleep(1);
    }
    c = getchar();
    printf("\nChar received:%c\n", c);
    printf("Done.\n");

    return 0;
}
4
ssinfod

Returnキーを押さずに入力を読み取るループが常に必要でした。これは私のために働いた。

#include<stdio.h>
 main()
 {
   char ch;
    system("stty raw");//seting the terminal in raw mode
    while(1)
     {
     ch=getchar();
      if(ch=='~'){          //terminate or come out of raw mode on "~" pressed
      system("stty cooked");
     //while(1);//you may still run the code 
     exit(0); //or terminate
     }
       printf("you pressed %c\n ",ch);  //write rest code here
      }

    }
3
Setu Gupta

以下はExpert C Programming:Deep Secretsから抽出されたソリューションで、SVr4で動作するはずです。 sttyおよびioctlを使用します。

#include <sys/filio.h>
int kbhit()
{
 int i;
 ioctl(0, FIONREAD, &i);
 return i; /* return a count of chars available to read */
}
main()
{
 int i = 0;
 intc='';
 system("stty raw -echo");
 printf("enter 'q' to quit \n");
 for (;c!='q';i++) {
    if (kbhit()) {
        c=getchar();
       printf("\n got %c, on iteration %d",c, i);
    }
}
 system("stty cooked echo");
}
3
PolyThinker

ncursesは、これを行う良い方法を提供します!また、これは私の最初の投稿(覚えている)なので、コメントは大歓迎です。便利なものに感謝しますが、すべて歓迎します!

コンパイルするには:g ++ -std = c ++ 11 -pthread -lncurses .cpp -o

#include <iostream>
#include <ncurses.h>
#include <future>

char get_keyboard_input();

int main(int argc, char *argv[])
{
    initscr();
    raw();
    noecho();
    keypad(stdscr,true);

    auto f = std::async(std::launch::async, get_keyboard_input);
    while (f.wait_for(std::chrono::milliseconds(20)) != std::future_status::ready)
    {
        // do some work
    }

    endwin();
    std::cout << "returned: " << f.get() << std::endl;
    return 0;
}

char get_keyboard_input()
{
    char input = '0';
    while(input != 'q')
    {
        input = getch();
    }
    return input;
}
2
AngryDane

windows上で私のために働く:

#include <conio.h>
char c = _getch();
2
user1438233

SDL(Simple DirectMedia Library)を使用して移植可能にできますが、その動作が気に入らないかもしれません。試したとき、SDLに新しいビデオウィンドウを作成して(プログラムには必要ありませんでした)、このウィンドウにほぼすべてのキーボードとマウスの入力を「グラブ」させる必要がありました(使用には問題ありませんが、他の状況では迷惑または実行不能になります)。完全な移植性が必須である場合を除いて、やり過ぎであり、価値がないと思われます。

ところで、もしあなたがそれに興味があるなら、これはキープレスとリリースイベントを別々に与えます。

1
Ruchira