web-dev-qa-db-ja.com

getchar()でEnterキーを押さないようにする方法

次のコードでは:

#include <stdio.h>

int main(void) {   
  int c;   
  while ((c=getchar())!= EOF)      
    putchar(c); 
  return 0;
}

私は押す必要があります Enter getcharで入力したすべての文字を印刷しますが、これを行いたくありません。文字を押して、押しずに繰り返し入力した文字をすぐに表示します Enter。たとえば、「a」という文字を押すと、その隣に別の「a」が表示されます。

aabbccddeeff.....

しかし、「a」を押しても何も起こりません。他の文字を書くことができ、押すとコピーが表示されます Enter

abcdef
abcdef

これどうやってするの?

コンパイルにはUbuntuでcc -o example example.cコマンドを使用しています。

67
javier

Linuxシステムでは、sttyコマンドを使用して端末の動作を変更できます。デフォルトでは、端末はすべての情報を Enter Cプログラムに送信する前に押されます。

プログラム自体の中から動作を変更するための、迅速で、汚く、特に移植性のない例:

#include<stdio.h>

int main(void){
  int c;
  /* use system call to make terminal send all keystrokes directly to stdin */
  system ("/bin/stty raw");
  while((c=getchar())!= '.') {
    /* type a period to break out of the loop, since CTRL-D won't work raw */
    putchar(c);
  }
  /* use system call to set terminal behaviour to more normal behaviour */
  system ("/bin/stty cooked");
  return 0;
}

これは、stty cookedは、元の端末設定が何であったかをチェックするのではなく、プログラムの終了時に必要な動作です。また、すべての特別な処理はrawモードでスキップされるため、多くのキーシーケンス(CTRL-CまたはCTRL-Dなど)は期待どおりに実際には機能しませんプログラムで明示的に処理せずに。

あなたはできる man stty達成したい内容に応じて、端末の動作をより細かく制御します。

62
goldPseudo

これはOSに依存します。UNIXのような環境ではデフォルトでICANONフラグが有効になっているため、入力は次の_'\n'_またはEOFまでバッファリングされます。正規モードを無効にすると、すぐにキャラクターを取得できます。これは他のプラットフォームでも可能ですが、単純なクロスプラットフォームソリューションはありません。

編集:私はあなたがUbuntuを使用することを指定したようです。昨日似たようなものを投稿しましたが、これにより端末の多くのデフォルト動作が無効になることに注意してください。

_#include<stdio.h>
#include <termios.h>            //termios, TCSANOW, ECHO, ICANON
#include <unistd.h>     //STDIN_FILENO


int main(void){   
    int c;   
    static struct termios oldt, newt;

    /*tcgetattr gets the parameters of the current terminal
    STDIN_FILENO will tell tcgetattr that it should write the settings
    of stdin to oldt*/
    tcgetattr( STDIN_FILENO, &oldt);
    /*now the settings will be copied*/
    newt = oldt;

    /*ICANON normally takes care that one line at a time will be processed
    that means it will return if it sees a "\n" or an EOF or an EOL*/
    newt.c_lflag &= ~(ICANON);          

    /*Those new settings will be set to STDIN
    TCSANOW tells tcsetattr to change attributes immediately. */
    tcsetattr( STDIN_FILENO, TCSANOW, &newt);

    /*This is your part:
    I choose 'e' to end input. Notice that EOF is also turned off
    in the non-canonical mode*/
    while((c=getchar())!= 'e')      
        putchar(c);                 

    /*restore the old settings*/
    tcsetattr( STDIN_FILENO, TCSANOW, &oldt);


    return 0;
}
_

すべての文字が2回表示されることに気付くでしょう。これは、入力がすぐに端末にエコーバックされ、プログラムがputchar()を使用してそれを戻すためです。入力と出力の関連付けを解除する場合は、ECHOフラグもオフにする必要があります。これを行うには、適切な行を次のように変更するだけです。

_newt.c_lflag &= ~(ICANON | ECHO); 
_
78
Lucas

getchar()は、プラットフォームがそのキーが押されるまで入力をバッファするため、多くのプラットフォームでEnterキーを押して入力を取得する必要がある標準関数です。多くのコンパイラ/プラットフォームは、ENTERを気にしない非標準のgetch()をサポートしています(プラットフォームのバッファリングをバイパスし、ENTERを別のキーのように扱います)。

7
Eric J.

I/Oはオペレーティングシステムの機能です。多くの場合、オペレーティングシステムは、Enterキーを押すまで、入力した文字をプログラムに渡しません。これにより、ユーザーはプログラムに送信する前に入力を変更できます(バックスペースや再入力など)。ほとんどの目的で、これはうまく機能し、ユーザーに一貫したインターフェイスを提供し、プログラムがこれに対処する必要を軽減します。場合によっては、プログラムがキーが押されたときにキーから文字を取得することが望ましい場合があります。

Cライブラリ自体はファイルを処理するため、入力ファイルにデータがどのように取り込まれるかは関係ありません。したがって、言語自体には、押されたキーを取得する方法はありません。代わりに、これはプラットフォーム固有です。 OSまたはコンパイラを指定していないため、検索することはできません。

また、標準出力は通常効率のためにバッファリングされます。これはCライブラリによって行われるため、Cソリューションがあります。これは、各文字が書き込まれた後にfflush(stdout);することです。その後、文字がすぐに表示されるかどうかはオペレーティングシステム次第ですが、私が使い慣れているすべてのOSがすぐに出力を表示するため、通常は問題になりません。

6
David Thornley

私はルーカスの回答が好きですが、少し詳しく説明したいと思います。 _termios.h_にはcfmakeraw()という名前の組み込み関数があり、manは次のように説明します。

_cfmakeraw() sets the terminal to something like the "raw" mode of the
old Version 7 terminal driver: input is available character by
character, echoing is disabled, and all special processing of
terminal input and output characters is disabled. [...]
_

これは基本的にLucasが提案したものと同じであり、manページで設定されている正確なフラグを見ることができます: termios(3)

使用事例

_int c = 0;
static struct termios oldTermios, newTermios;

tcgetattr(STDIN_FILENO, &oldTermios);
newTermios = oldTermios;

cfmakeraw(&newTermios);

tcsetattr(STDIN_FILENO, TCSANOW, &newTermios);
c = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldTermios);

switch (c) {
    case 113: // q
        printf("\n\n");
        exit(0);
        break;
    case 105: // i
        printf("insert\n");
        break;
    default:
        break;
_
4
Patrik Höggren

Unix派生物(Ubuntu)で作業しているため、これを行う1つの方法を次に示します-推奨されませんが、機能します(コマンドを正確に入力できる限り)。

echo "stty -g $(stty -g)" > restore-sanity
stty cbreak
./your_program

プログラムに飽きたら、割り込みを使用してプログラムを停止します。

sh restore-sanity
  • 'echo'行は、現在の端末設定を復元するシェルスクリプトとして保存します。
  • 「stty」行は、ほとんどの特別な処理をオフにします( Control-D たとえば、効果はなく、文字が利用可能になり次第、プログラムに文字を送信します。これは、入力をこれ以上編集できないことを意味します。
  • 「sh」行は、元の端末設定を復元します。

'stty sane'が目的に対して設定を十分に正確に復元する場合、節約できます。 '-g'の形式は、 'stty'のバージョン間で移植性がありません(したがって、Solaris 10で生成されたものはLinuxでは機能しません、またはその逆)。 「stty sane」オプションは、一般に入手できるものではありませんが、知る限り(Linuxでは)。

4

はい、Windowsでもこれを行うことができます。conio.hライブラリを使用して以下のコードを作成します

#include <iostream> //basic input/output
#include <conio.h>  //provides non standard getch() function
using namespace std;

int main()
{  
  cout << "Password: ";  
  string pass;
  while(true)
  {
             char ch = getch();    

             if(ch=='\r'){  //when a carriage return is found [enter] key
             cout << endl << "Your password is: " << pass <<endl; 
             break;
             }

             pass+=ch;             
             cout << "*";
             }
  getch();
  return 0;
}
2
djaa2807

「ncurses」ライブラリをインクルードし、getch()の代わりにgetchar()を使用できます。

2
AShelly

私が現在取り組んでいる課題でこの問題/疑問が出てきました。また、どの入力から取得するかに依存します。私は使っている

/dev/tty

プログラムの実行中に入力を取得するため、コマンドに関連付けられたファイルストリームである必要があります。

テスト/ターゲットにする必要があるubuntuマシンでは、それ以上のものが必要でした

system( "stty -raw" );

または

system( "stty -icanon" );

次のように、コマンドへのパスと同様に--fileフラグを追加する必要がありました。

system( "/bin/stty --file=/dev/tty -icanon" );

すべてが今では協力的です。

1
Chad Chabot