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


174

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

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


1
@adam-明確にできますか:使用可能な文字がない場合にすぐに戻る関数、または常に単一のキー入力を待つ関数にしたいですか?
Roddy、

2
@Roddy-常に1回のキー入力を待機する関数が必要です。
アダム

回答:


99

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

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

  2. Linuxで利用できるcurses。Windowsでも互換性のあるcurses実装を利用できます。getch()機能もあります。(man getchそのマンページを表示してみてください)。ウィキペディアのCursesを参照してください。

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


7
現在、ncursesはの推奨されるバリアントであることに注意してくださいcurses
ファンドモニカの訴訟

4
ライブラリが必要な場合、どのようにして作成しましたか?ライブラリを作成するには、確実にそれをプログラムする必要があり、それはだれでもプログラムできることを意味するので、なぜ私たちが必要とするライブラリコードだけをプログラムできないのでしょうか。それもなるだろう純粋なCに移植性可能性がないことを++間違っ
OverloadedCore

@ kid8わからない
Johannes Schaub-litb

1
@ JohannesSchaub-litb彼はおそらく、あなたが不可能だと言ったときに、ライブラリ実装者が純粋なc / c ++でその移植可能なメソッドをどのように作成したのかを言おうとしています。
Abhinav Gauniyal 2017年

@AbhinavGauniyalああ、なるほど。ただし、ライブラリの実装者が移植可能なメソッドを作成していない(つまり、適度に移植可能なプログラムで使用される)とは言っていません。私は、彼らが移植可能なC ++を使用していないことを示唆しています。彼らがそうしていないことは明らかですが、彼のコメントが私の答えのこの部分が間違っていると言っている理由がわかりません。
ヨハネスシャウブ-litb 2017年

81

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);
}

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


このコードを実装しようとしましたが、への呼び出しでエラーが発生しましたread。両方のヘッダーを含めました。
マイケルドースト

6
read()がunistd.hで定義されたPOSIXシステムコールであるため、このコードが間違っている可能性があります。stdio.hは偶然それをインクルードするかもしれませんが、実際にはこのコードにstdio.hはまったく必要ありません。unistd.hに置き換えれば問題ありません。
Falcon Momot

1
Raspberry PiのROS端末でキーボード入力を取得しようとしていたとき、どうやってここに到達するのかわかりません。このコードスニペットは私にとってはうまくいきます。
aknay 2015年

2
@FalconMomot私のNetBeans IDE 8.1(Kali Linuxの場合)には次のように書かerror: ‘perror’ was not declared in this scopestdio.hていunistd.hます。
Abhishek Kashyap

3
これをブロックしないように拡張する簡単な方法はありますか?
ジョーストラウト2017

18

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

#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; 
}

13
これは機能しますが、それだけの価値があるので、システムへのシェルアウトはめったに「最良の」方法ではありません。sttyプログラムはCで記述されているため、外部プログラム/ fork / whatnotに依存することなく、<termios.h>または<sgtty.h>を含めて、sttyが使用するのと同じコードを呼び出すことができます。
Chris Lutz、

1
私はこれをランダムに概念実証したり、いじったりするために必要でした。ちょうど私が必要としたもの。ありがとうございました。注意:私は間違いなくsttyをプログラムの最後に調理します。そうしないと、シェルはstty rawモードのままになり、プログラムが停止した後、基本的にシェルが壊れました。
ベンジャミン

忘れたと思います#include <stdlib.h>
NerdOfCode

14

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

または

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


9

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

正直に言うと、これは私が持っている古いコードからのものなので、少しいじる必要があります。

ただし、何もプロンプトを表示せずに入力を読み取るため、文字がまったく表示されないというのはすばらしいことです。


9
#include <conio.h>

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

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


5
conio.h?「conio.hは、古いMS-DOSコンパイラでテキストユーザーインターフェイスを作成するために使用されるCヘッダーファイルです。」やや古くなっているようです。
わかりました-SEは


5

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

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


5

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

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

  • initscr そして endwin
  • cbreak そして nocbreak
  • getch

幸運を!


3

以下は、SVr4で動作するはずのエキスパートCプログラミング:ディープシークレットから抽出したソリューションです。sttyioctlを使用します

#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

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
      }

    }

1
RAWモードになると、プロセスを終了するのが難しくなります。そのため、「〜を押したとき」と同じようにプロセスを強制終了するようにしてください。................それ以外の場合は、KILLを使用して他の端末からプロセスを強制終了できます。
Setu Gupta

3

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;
}


1

SDL(Simple DirectMedia Library)を使用すると、移植性の高い方法で実行できますが、その動作は気に入らないかもしれません。試してみると、SDLで新しいビデオウィンドウを作成し(プログラムには必要ありませんでした)、このウィンドウにほとんどすべてのキーボードとマウスの入力を「グラブ」させる必要がありました(これは私の使用では問題ありませんでしたが)他の状況では煩わしい、または機能しない。完全な移植性が必須でない限り、それはやり過ぎであり、価値がないと思います。それ以外の場合は、他の推奨される解決策の1つを試してください。

ちなみに、これにより、キープレスとリリースのイベントを別々に行うことができます。


1

これはシステムにシェルアウトしないバージョンです(macOS 10.14で作成およびテストされています)

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

char* getStr( char* buffer , int maxRead ) {
  int  numRead  = 0;
  char ch;

  struct termios old = {0};
  struct termios new = {0};
  if( tcgetattr( 0 , &old ) < 0 )        perror( "tcgetattr() old settings" );
  if( tcgetattr( 0 , &new ) < 0 )        perror( "tcgetaart() new settings" );
  cfmakeraw( &new );
  if( tcsetattr( 0 , TCSADRAIN , &new ) < 0 ) perror( "tcssetattr makeraw new" );

  for( int i = 0 ; i < maxRead ; i++)  {
    ch = getchar();
    switch( ch )  {
      case EOF: 
      case '\n':
      case '\r':
        goto exit_getStr;
        break;

      default:
        printf( "%1c" , ch );
        buffer[ numRead++ ] = ch;
        if( numRead >= maxRead )  {
          goto exit_getStr;
        }
        break;
    }
  }

exit_getStr:
  if( tcsetattr( 0 , TCSADRAIN , &old) < 0)   perror ("tcsetattr reset to old" );
  printf( "\n" );   
  return buffer;
}


int main( void ) 
{
  const int maxChars = 20;
  char      stringBuffer[ maxChars+1 ];
  memset(   stringBuffer , 0 , maxChars+1 ); // initialize to 0

  printf( "enter a string: ");
  getStr( stringBuffer , maxChars );
  printf( "you entered: [%s]\n" , stringBuffer );
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.