web-dev-qa-db-ja.com

getchar()を理解しようとしています!= EOF

私はCプログラミング言語を読んでおり、これまでのところすべてを理解しています。ただし、getchar()putchar()に出会ったとき、それらの使用法、具体的には次のコードの動作を理解できませんでした。

_main()
{
    int c;
    while ((c = getchar()) != EOF)
       putchar(c);
}
_

main()関数、整数の宣言cおよびwhileループを理解しています。それでも、whileループ内の条件について混乱しています。このCコードの入力と出力は何ですか。

これが基本的で愚かな質問であれば申し訳ありませんが、本を読み進めて混乱する前に、簡単な説明を探しています。

30

getchar()は、標準入力から文字を読み取る関数です。 EOFは、[〜#〜] c [〜#〜]で使用される特殊文字で、END OF FILEに到達したことを示します。

通常、標準入力がコンソール(ファイルなど)以外の場合、getchar()からEOF文字が返されます。

このようにプログラムをUNIXで実行する場合:

_$ cat somefile | ./your_program
_

getchar()は、somefileが終了するとすぐにEOFおよびsomefileのすべての文字を返します。

このようにプログラムを実行する場合:

_$ ./your_program
_

そして、EOFをコンソールから送信します(Unixでは_CTRL+D_を、WindowsではCTRL + Zを押すことで)、getchar()EOFと実行を返します。終了します。

18

コマンドラインに-1を入力してもプログラムが終了しないという事実に戸惑うかもしれません。 getchar()はこれを2つの文字、-と1として読み取るため、cへの割り当てで、文字はASCII数値に変換されます。この数値は、 cによってアクセスされるメモリの場所。

次に、putchar(c)がこの値を取得し、ASCIIテーブルを検索し、文字に変換して出力します。

テーブルが0から始まるため、ASCIIテーブルで値-1 10進数を見つけることは不可能です。したがって、getchar()は異なるプラットフォームで異なるソリューションを考慮する必要があります。プラットフォームごとにgetchar()バージョンがありますか?

このEOFが通常のasciiにないことは奇妙だと思います。印刷できない最初の文字の1つである可能性があります。たとえば、行末はASCII。

WindowsからLinuxにファイルを転送するとどうなりますか? EOFファイル文字は自動的に更新されますか?

4
Pedro Norwego

getchar() 関数はキーボードから文字を読み取ります(つまり、stdin

指定されたwhileループ内の条件では、各反復の前にgetchar()が呼び出され、受信した値が整数cに割り当てられます。

さて、Cでは、標準入力(stdin)は like ファイルであることを理解する必要があります。つまり、入力はバッファリングされます。入力は、実際に消費されるまでバッファに残ります。 stdinは、実際には 標準入力ストリーム です。

getchar()は、入力バッファで次に使用可能な値を返します。

プログラムは基本的に、キーボードから読み取られたものをすべて表示します。 \n(改行)、スペースなどの空白を含む.

つまり、入力はユーザーがキーボードを介して提供する入力です(stdinは通常キーボードを意味します)。そして、出力は入力として提供するものです。

提供する入力は文字ごとに読み取られ、数字として与えられた場合でも文字として扱われます。

getchar()は、ファイルの終わりに達した場合にのみEOFを返します。ここで関心のある「ファイル」はstdin自体(標準入力)です。

キーボードを介して提供する入力が保存されているファイルが存在することを想像してください。 stdinです。この「ファイル」は 無限のファイル のようなものです。したがって、EOFはありません。

getchar()が一度に処理できる以上の入力を提供する場合(Enterを押して入力として提供する前)、余分な値は入力バッファに消費されずに格納されます。 getchar()は入力から最初の文字を読み取り、c and printcwithputchar(c) `に保存します。

whileループの次の反復中に、前の反復中に指定された、まだstdinにある余分な文字は、while ((c = getchar()) != EOF)の間にc=getchar()部分とともに取得されます。入力バッファに何もなくなるまで同じプロセスが繰り返されます。

これにより、反復中に複数の文字が入力として与えられた場合、putchar()が一度に1文字ではなく文字列を返しているように見えます。

例:入力が
abcdefghijkl
出力は同じでした
abcdefghijkl

この動作が望ましくない場合は、putchar(c);の直後に fflush(stdin); を追加できます。これにより、ループは各反復中に提供された入力の最初の文字のみを出力します。

例:入力が
adgbad
aのみが出力されます。

Enterキーを押した後にのみ、入力はstdinに送信されます。

putchar()getchar()の反対です。出力を標準出力ストリーム(stdout、通常はモニター)に書き込みます。

EOFは、ファイルに存在する文字ではありません。関数によってエラーコードとして返されたものです。

ただし、通常はgive whileループを終了することはできません。入力バッファは、キーボードから何かが入力されるとすぐに(出力に表示するために)空になり、stdinEOFを与えません。

ループを手動で終了するには、キーボードを使用してEOFを送信します。 ctrl+D Linuxおよび
ctrl+Z Windowsで

例えば:

while ((c = getchar()) != EOF)
{

   putchar(c);
   fflush(stdin);
}
printf("\nGot past!");

キーの組み合わせを押してEOFを指定すると、プログラムを終了する前にメッセージGot past!が表示されます。

stdinが じゃない すでに空の場合は、このキーの組み合わせを2回押す必要があります。このバッファーをクリアしてからEOFをシミュレートします。

編集:c = getchar()while ((c = getchar()) != EOF)を囲む括弧の余分なペアは、getchar()によって返される値が最初にcに割り当てられることを確認することです。 それ 値はEOFと比較されます。

この余分な括弧がない場合、式は実質的にwhile (c = (getchar() != EOF) )になり、cは次の2つの値を持つことができます:1(trueの場合)または0(forの場合) false)これは明らかに意図したものではありません。

3
J...S

現在のC標準で書かれたコードは

_#include <stdio.h>

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

ループは次のように書き直すことができます

_int c;
while (1) {
    c = getchar();
    if (c != EOF)
        putchar(c);
    else
        break;
}
_

これは

  • 永遠に繰り返す
    • standard input から入力の次の文字(「バイト」)を取得し、cに格納する
    • 上記の文字の読み取り中に例外条件が発生しなかった場合
      • cに保存されている文字を標準出力に出力します
    • その他
      • ループを破る

多くのプログラミング言語は、例外を発生させる/を介して、通常のプログラムフローを中断する例外的な条件を処理します。 Cはそのようなことをしません。代わりに、失敗する可能性のある関数には戻り値があり、例外条件は特別な戻り値によって通知されます。この戻り値は、指定された関数のドキュメントから確認する必要があります。 getcharの場合、C11標準のドキュメントには( C11 7.21.7.6p )と書かれています:

  1. getchar関数は、stdinが指す入力ストリームから次の文字を返します。ストリームがファイルの終わりにある場合、ストリームのファイルの終わりインジケータが設定され、getcharEOFを返します。読み取りエラーが発生すると、ストリームのエラーインジケーターが設定され、getcharEOFを返します。

EOFは0未満の整数定数であり、通常の戻り値は0以上である-_unsigned char_はintにゼロ拡張されていると他の場所で述べられています。

ファイルの終わりにあるストリームは、すべての入力が消費されたことを意味します。標準入力の場合、次のように入力してキーボードからこれを引き起こすことができます。 Ctrl+D Unix/Linux端末および Ctrl+Z Windowsコンソールウィンドウで。別の可能性は、プログラムがキーボードからではなくファイルまたはパイプから入力を受信することです。その入力が完全に消費されるたびに、ファイルの終わりが通知されます。

_cat file | ./myprogram
_

または

_./myprogram < file
_

上記のフラグメントが言うように、実際にはgetcharEOFを返す2つの異なる条件があります。 end-of-file に到達したか、 or 実際のエラーが発生しました。これは、戻り値だけから推測することはできません。代わりに、関数feofおよびferrorを使用する必要があります。 feof(stdin)は、標準入力でファイルの終わりに達した場合に真の値を返します。エラーが発生した場合、ferror(stdin)はtrueを返します。

実際のエラーが発生した場合、_<errno.h>_で定義された変数errnoにはエラーコードが含まれます。関数perrorを使用すると、人間が読み取れるエラーメッセージに接頭辞を自動的に表示できます。したがって、例を次のように展開できます。

_#include <stdio.h>
#include <errno.h> // for the definition of errno
#include <stdlib.h> // for exit()
int main(void)
{
    int c;
    while ((c = getchar()) != EOF)
       putchar(c);

    if (feof(stdin)) {
        printf("end-of-file reached\n");
        exit(0);
    }
    else if (ferror(stdin)) {
        printf("An error occurred. errno set to %d\n", errno);
        perror("Human readable explanation");
        exit(1);
    }
    else {
        printf("This should never happen...\n");
        exit('?');
    }
}
_

ファイルの終わりをトリガーするには、Linuxの新しい行でCtrl + D(ここでは_^D_と表示)を使用します。

_% ./a.out
Hello world
Hello world
^D
end-of-file reached
_

(ここで input がどのように行バッファリングされるかに注意してください。したがって、入力は出力内の行内でインターリーブされません)。

同様に、パイプラインを使用して同じ効果を得ることができます。

_% echo Hello world | ./a.out
Hello world
end-of-file reached
_

エラーをトリガーすることはもう少し難しいです。 bashおよびzshシェルでは、コマンドに_<&-_を追加することにより、どこからでも来ないように、標準入力を closed にできます。ライン:

_% ./a.out <&-
An error occurred. errno set to 9
Human readable explanation: Bad file descriptor
_

不良ファイル記述子、またはEBADFは、標準入力-ファイル記述子番号0が開かれていないため無効であったことを意味しますまったく。

エラーを生成する別の楽しい方法は、 directory から標準入力を読み取ることです。これにより、LinuxでerrnoがEISDIRに設定されます。

_% ./a.out < / 
An error occurred. errno set to 21
Human readable explanation: Is a directory
_

実際には、putcharの戻り値もチェックする必要があります。同様に、エラー時にEOFを返すか、書き込まれた文字を返します。

_while ((c = getchar()) != EOF) {
    if (putchar(c) == EOF) {
        perror("putchar failed");
        exit(1);
    }
}
_

そして今、標準出力を_/dev/full_にリダイレクトすることでこれをテストすることができます-しかし、落とし穴があります-標準出力はバッファリングされているので、 enough を記述してバッファをプログラムの最後ではなく、すぐにフラッシュします。 _/dev/zero_から無限のゼロバイトを取得します:

_ % ./a.out < /dev/zero > /dev/full
 putchar failed: No space left on device
_

追伸getchar()の戻り値を格納するには、常にint型の変数を使用することが非常に重要です。 文字を読み取りますが、 signed/unsigned/plain charは常に間違っています

3
Antti Haapala
main(){
int c;
while ((c = getchar()) != EOF)
   putchar(c);
}

実際には、c = getchar()はユーザーがコンソールに入力する文字を提供し、その値はEOF End Of Fileを表します。EOF最後のファイル。(c = getchar())!= EOFはc!= EOFと同等です。今ではこれがはるかに簡単だと思います。さらにクエリがある場合はお知らせください。

0
Shivam Goel
_ getchar()
_

入力から文字を取得します。

_ c = getchar()
_

この割り当ての値は、割り当て後の左側の値、または読み取られた文字の値です。 EOFの値はデフォルトで_-1_です。

_ ((c = getchar()) != EOF)
_

値がEOF以外の値である限り、つまり、条件がtrueである限り、ループは反復し続けます。値がEOFになると、条件全体の値は_0_になり、ループが中断されます。

c = getchar()の周りの追加の括弧は、通常__==_を入力することを想定して警告するため、条件内で実際に割り当てを行いたいことを強調するためのコンパイラー用です。

_ main() {
     int c;
     while ((c = getchar()) != EOF)
         putchar(c);
 }
_

したがって、コード全体が実際に入力内容をエコーバックします。文字の値を条件内のcに割り当ててから、ループの本体に出力し、ファイルの終わりが検出された場合にのみ終了します。

0

|と同様の方法で上記のパイプコマンドを使用すると、システムでリダイレクトを使用して、上記のコードを使用して、ファイルのすべての文字コンテンツを、通常はCTRL-ZまたはCTRL-Dで表される末尾(EOF)に達するまで表示できます。

コンソールで:ProgramName < FileName1.txt

FileName1から読み取ったもののコピーを作成するには、次のようにします。ProgramName < FileName1.txt > CopyOfInput.txt

これは、理解を助けるために、プログラムを複数の方法で示しています。

-助けてくれることを願っています。