84

次のコードでは:

#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

これどうやってするの?

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

4

13 に答える 13

104

これは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); 
于 2009-11-25T18:03:26.680 に答える
74

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

プログラム自体の内部から動作を変更するための、すばやく汚い、特に移植性のない例:

#include<stdio.h>
#include<stdlib.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-CCTRL-Dなど)は、プログラムで明示的に処理しないと、期待どおりに機能しません。

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

于 2009-11-25T18:31:10.960 に答える
13

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

于 2009-11-25T17:23:08.340 に答える
6

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

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

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

于 2009-11-25T17:32:56.117 に答える
6

ルーカスの答えが好きですが、少し詳しく説明したいと思います。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;
于 2016-07-11T21:15:55.250 に答える
4

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

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

あなたがそれに飽きたら、割り込みを使ってプログラムを止めてください。

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

'sttysane'が目的に十分正確に設定を復元するかどうかを節約できます。'-g'の形式は'stty'のバージョン間で移植できません(したがって、Solaris 10で生成されたものはLinuxでは機能しません。その逆も同様です)が、この概念はどこでも機能します。'stty sane'オプションは、普遍的に利用できるわけではありません、AFAIK(ただし、Linux上にあります)。

于 2009-11-25T18:23:11.897 に答える
2

'ncurses'ライブラリを含めて、のgetch()代わりに使用することができますgetchar()

于 2009-11-25T19:20:33.000 に答える
2

「で押すのを避ける方法Entergetchar()

まず第一に、端末入力は通常、ラインまたは完全にバッファリングされています。これは、オペレーティングシステムが端末からの実際の入力をバッファに保存することを意味します。通常、このバッファは、fe\nがでシグナリング/提供されたときにプログラムにフラッシュされstdinます。これは、へのプレスによって作られたfeEnterです。

getchar()チェーンの最後にあります。バッファリングプロセスに実際に影響を与えることはできません。


どうすればこれができますか?

他の回答getchar()でよく説明されているように、特定のシステムコールを使用して端末の動作を明示的に変更したくない場合は、そもそも捨ててください。

残念ながら、標準ライブラリ関数はなく、単一文字入力でバッファをフラッシュするポータブルな方法もありません。ただし、実装ベースの非ポータブルソリューションがあります。


Windows / MS-DOSでは、ヘッダーファイルにgetch()andgetche()関数がconio.hあり、必要なことを正確に実行します。改行がバッファをフラッシュするのを待たずに、1文字を読み取ります。

getch()との主な違いは、コンソールに実際の入力文字がすぐに出力されないgetche()ことですが、出力されます。追加はechoの略です。getch()getche()"e"

例:

#include <stdio.h>
#include <conio.h>   

int main (void) 
{
    int c;

    while ((c = getche()) != EOF)
    {
        if (c == '\n')
        {
            break;
        }

        printf("\n");
    }

    return 0;
}

Linuxでは、文字の直接処理と出力を取得する方法は、ncurses-libraryのcbreak()andecho()オプションとgetch()andルーチンを使用することです。refresh()

いわゆる標準画面をで初期化し、ルーチンinitscr()で閉じる必要があることに注意してください。endwin()

例:

#include <stdio.h>
#include <ncurses.h>   

int main (void) 
{
    int c;

    cbreak(); 
    echo();

    initscr();

    while ((c = getch()) != ERR)
    {
        if (c == '\n')
        {
            break;
        }

        printf("\n");
        refresh();
    }

    endwin();

    return 0;
}

-lncurses注:リンカーがncurses-libraryを検索して見つけることができるように、オプションを指定してコンパイラーを呼び出す必要があります。

于 2020-05-29T15:35:06.600 に答える
1

はい、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;
}
于 2011-06-10T15:36:18.417 に答える
1

私は現在取り組んでいる課題でこの問題/質問が出てきました。また、取得している入力によっても異なります。使ってます

/dev/tty

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

私がテスト/ターゲットする必要があるubuntuマシンでは、それは単なる

system( "stty -raw" );

また

system( "stty -icanon" );

次のように、コマンドへのパスだけでなく、-fileフラグも追加する必要がありました。

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

今ではすべてが共食いです。

于 2012-02-14T17:39:34.063 に答える
0

このコードは私のために働いた。注意:ほとんどのコンパイラー(私はGCCを使用)がサポートしている場合でも、これは標準ライブラリーの一部ではありません。

#include <stdio.h>
#include <conio.h>
int main(int argc, char const *argv[]) {
    char a = getch();
    printf("You typed a char with an ASCII value of %d, printable as '%c'\n", a, a);
    return 0;
}

このコードは、最初のキー押下を検出します。

于 2019-12-07T20:58:49.377 に答える
0

以下をチェックする新しい関数を作成できますEnter

#include <stdio.h>

char getChar()
{
    printf("Please enter a char:\n");

    char c = getchar();
    if (c == '\n')
    {
        c = getchar();
    }

    return c;
}

int main(int argc, char *argv[])
{
    char ch;
    while ((ch = getChar()) != '.')
    {
        printf("Your char: %c\n", ch);
    }

    return 0;
}
于 2021-02-02T16:11:49.993 に答える
-1

デフォルトでは、Cライブラリはリターンが表示されるまで出力をバッファリングします。結果をすぐに印刷するには、次を使用しますfflush

while((c=getchar())!= EOF)      
{
    putchar(c);
    fflush(stdout);
}
于 2009-11-25T17:22:50.650 に答える