[解決済み] while ( !feof (file) ) 」は、なぜいつも間違っているのですか?
質問
を使用すると、何が問題なのでしょうか?
feof()
を使用して、読み取りループを制御することはできますか? 例えば
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char **argv)
{
char *path = "stdin";
FILE *fp = argc > 1 ? fopen(path=argv[1], "r") : stdin;
if( fp == NULL ){
perror(path);
return EXIT_FAILURE;
}
while( !feof(fp) ){ /* THIS IS WRONG */
/* Read and process data from file… */
}
if( fclose(fp) != 0 ){
perror(path);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
このループのどこが悪いのでしょうか?
解決方法は?
TL;DR
while(!feof)
は、無関係なものをテストし、知る必要のあるものをテストしていないため、間違っています。その結果、読み込みに成功したデータにアクセスしていると思い込んでいるコードを誤って実行することになります。
抽象的でハイレベルな視点を提供したいと思います。ということに興味がある方は読み進めてください。
while(!feof)
を実際に行っています。
並行性と同時性
I/O操作は環境と相互作用します。環境はあなたのプログラムの一部でもなければ、あなたのコントロール下でもありません。環境は本当にあなたのプログラムと "同時に"存在するのです。すべての並行処理がそうであるように、「現在の状態」についての質問は意味をなさない。同時発生するイベントには「同時性」という概念はありません。状態の多くの特性は、単に 存在する を同時進行させる。
もう少し正確に説明します。例えば、「もっとデータがあるのか」と尋ねるとします。これを同時実行コンテナやI/Oシステムに尋ねることができます。しかし、その答えは一般的にアクション不可能であり、したがって意味がありません。コンテナが「はい」と答えたらどうなるでしょうか。同様に、もし答えが「いいえ」であったとしても、あなたが読もうとしたときにはデータが届いているかもしれません。結論は、単純に は のような性質はありません。なぜなら、どのような答えが返ってきても、それに対して意味のある行動をとることができないからです。(バッファリングされた入力では、ある種の保証を構成する "yes, I have data" が得られる可能性があり、状況は少し良くなりますが、それでも逆のケースに対処できるようにしなければならないでしょう)。出力に関しては、私が説明したように、状況は確かに悪いです。)
ということで、不可能であり、実際には不可能であると結論づけました。 合理的 という問いかけをI/Oシステムに対して行うことです。 になります。 を実行することができます。これと対話できる唯一の方法は(同時実行コンテナと同じように)、次のとおりです。 試みる を実行し、成功したか失敗したかを確認します。環境と対話するその瞬間に、その対話が実際に可能であったかどうかを知ることができ、その時点で対話の実行をコミットしなければならない。(そしてその時点で、あなたはそのインタラクションを実行することを約束しなければなりません(これは、言うなれば「同期ポイント」です)。
EOF
さて、次はEOFです。EOFは 応答 から得られる 試される I/O操作。これは、何かを読み書きしようとしたが、その際にデータの読み書きに失敗し、代わりに入力または出力の終了に遭遇したことを意味します。これは、Cの標準ライブラリ、C++のiostreams、その他のライブラリなど、基本的にすべてのI/O APIに当てはまります。I/O操作が成功しさえすれば、単に 知ることができない さらに将来の操作が成功するかどうか。あなたは しなければならない は、必ず最初に操作を試し、成功か失敗かを応答する。
使用例
それぞれの例で、以下の点に注意してください。 まず I/O操作を試行し その後 その結果が有効であれば、それを消費する。さらに、我々は 常に はI/O操作の結果を使わなければならないが、その結果はそれぞれの例で異なる形や形態をとる。
-
C stdioで、ファイルから読み込む。
for (;;) { size_t n = fread(buf, 1, bufsize, infile); consume(buf, n); if (n == 0) { break; } }
私たちが使わなければならない結果は
n
は、読み込まれた要素の数(0になることもある)です。 -
C stdioです。
scanf
:for (int a, b, c; scanf("%d %d %d", &a, &b, &c) == 3; ) { consume(a, b, c); }
の戻り値を使用する必要があります。
scanf
は、変換された要素の数です。 -
C++、iostreams フォーマットの抽出。
for (int n; std::cin >> n; ) { consume(n); }
私たちが使わなければならない結果は
std::cin
これはブーリアンコンテキストで評価され、ストリームがまだgood()
の状態になります。 -
C++、iostreams getline。
for (std::string line; std::getline(std::cin, line); ) { consume(line); }
私たちが使用しなければならない結果は、再び
std::cin
先ほどと同じように -
POSIXです。
write(2)
でバッファをフラッシュします。char const * p = buf; ssize_t n = bufsize; for (ssize_t k = bufsize; (k = write(fd, p, n)) > 0; p += k, n -= k) {} if (n != 0) { /* error, failed to write complete buffer */ }
ここで使用する結果は
k
は、書き込まれたバイト数です。ここで重要なのは、何バイト書き込まれたかを知ることができるのは 後 書き込み操作を行った。 -
POSIX
getline()
char *buffer = NULL; size_t bufsiz = 0; ssize_t nbytes; while ((nbytes = getline(&buffer, &bufsiz, fp)) != -1) { /* Use nbytes of data in buffer */ } free(buffer);
私たちが使わなければならない結果は
nbytes
は、改行(ファイルが改行で終わっていない場合はEOF)までのバイト数です。この関数は、明示的に
-1
(エラーが発生したときやEOFに到達したときは、(EOFではなく!)。
EOF"という単語を実際に綴ることはほとんどないことにお気づきでしょう。通常、私たちはもっとすぐに興味を引かれるような他の方法でエラー状態を検出します(例えば、望んでいたほど多くのI/Oを実行できなかった場合など)。どの例でも、EOF状態に遭遇したことを明示的に伝えることができるAPI機能がありますが、これは実際にはあまり有用な情報ではありません。私たちがしばしば気にするよりも、ずっと細かいことなのだ。重要なのは、I/Oが成功したかどうかであり、どのように失敗したかよりも重要なのです。
-
最後に、実際にEOFの状態を問い合わせる例を挙げます。文字列があり、それが空白以外の余分なビットがない、完全な整数を表していることをテストしたいとします。C++のiostreamsを使うと、このようになります。
std::string input = " 123 "; // example std::istringstream iss(input); int value; if (iss >> value >> std::ws && iss.get() == EOF) { consume(value); } else { // error, "input" is not parsable as an integer }
ここでは2つの結果を使用します。1つ目は
iss
にフォーマットされた抽出が、ストリーム・オブジェクトそのものであることを確認するためです。
value
が成功しました。しかし、その後、空白も消費した後、別のI/O/操作を実行します。
iss.get()
これは、文字列全体がすでにフォーマットされた抽出によって消費されている場合に起こります。
C の標準ライブラリでは、同様のことを
strto*l
関数は、エンドポインタが入力文字列の末尾に到達したことを確認することによって、入力文字列の末尾に到達したことを確認します。
関連
-
[C] Error [Error] 代入の左オペランドとして lvalue が必要です。
-
[解決済み] あるJavaScriptファイルを他のJavaScriptファイルにインクルードするにはどうすればよいですか?
-
[解決済み] ファイルへの追記はどのように行うのですか?
-
[解決済み] Gitで1つのファイルの作業コピーの変更を元に戻す?
-
[解決済み] なぜテキストファイルは改行で終わらなければならないのですか?
-
[解決済み] 配列の場合、なぜ a[5] == 5[a] になるのでしょうか?
-
[解決済み] ファイルの内容からJavaの文字列を作成するにはどうすればよいですか?
-
[解決済み] Do-whileループをエミュレートする方法は?
-
[解決済み] C言語でランダムなint型を生成するには?
-
[解決済み] longをフォーマットするprintfの引数は何ですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
_CRT_SECURE_NO_WARNINGS エラーメッセージ、解決方法
-
libc++abi.dylib: NSException タイプの捕捉されない例外で終了するエラー
-
#137: 式は変更可能なlvalueでなければならない問題 // 文字列配列の代入問題
-
Solve Dev-c++ [エラー] 'for' ループの初期宣言は、C99 または C11 モードでのみ許可されます。
-
error: 'for' loop initial declaration is only allowed in C99 mode 原因と解決方法
-
未定義の `__isoc99_sscanf' への参照
-
コンパイルエラー:可変長オブジェクトが初期化されていない可能性があります。
-
[解決済み] PTHREAD_MUTEX_INITIALIZER vs pthread_mutex_init ( &mutex, param )
-
[解決済み] Cコードの単体テスト【終了しました
-
[解決済み] mallocとcallocの違い?