問題のプログラムのこのバリエーションを検討してください。
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
char *file = "D:\\data.txt";
FILE *fp;
char *formats[] =
{
"%d%d%d%*c",
"%d%d%d",
"%*c%d%d%d",
};
if (argc > 1)
file = argv[1];
for (int i = 0; i < 3; i++)
{
if ((fp = fopen(file, "r")) == 0)
{
fprintf(stderr, "Failed to open file %s\n", file);
break;
}
printf("Format: %s\n", formats[i]);
int n1,n2,n3;
while (fscanf(fp, formats[i], &n1, &n2, &n3) == 3)
printf("%d, %d, %d\n", n1, n2, n3);
fclose(fp);
}
return 0;
}
繰り返し開くのは効率的ではありませんが、ここでは問題ありません。明確さと行動を示すことは、はるかに重要です。
これは、(a) コマンド ラインで指定されたファイル名を使用するように書かれているため、Unix システムで作成するのに非常に不便な名前などをいじる必要がなくD:\data.txt
、(b) 使用されている 3 つの形式が示されています。
質問からのデータファイルを考えると:
243 343 434
393 322 439
984 143 943
438 243 938
プログラムの出力は次のとおりです。
Format: %d%d%d%*c
243, 343, 434
393, 322, 439
984, 143, 943
438, 243, 938
Format: %d%d%d
243, 343, 434
393, 322, 439
984, 143, 943
438, 243, 938
Format: %*c%d%d%d
43, 343, 434
393, 322, 439
984, 143, 943
438, 243, 938
最初の数値の最初の桁は、%*c
それがフォーマットの最初の部分であるときに消費されることに注意してください。最初の 3 つの数値が読み取ら%*c
れた後、 は行の 3 番目の数値の後の改行を読み取り、%d
さらに空白をスキップして (何もないことを除いて) 数値を読み取ります。
それ以外の場合、動作は以下の解説で説明されているとおりであり、別の関連する質問から主に取り上げられています。
関連する質問Use fscanf()
to read from given lineで議論されているコードの一部は次のとおりです。
fscanf(f, "%*d %*d %*d%*c");
fscanf(f, "%d%d%d", &num1, &num2, &num3);
コードは からの戻り値をテストする必要があることに注意しましたfscanf()
。ただし、3 つの%*d
変換指定では、指定された行に到達する前に EOF に遭遇すると、EOF の戻り値を取得する可能性があります。残念ながら、2 行目を実行するまで、最初の行に数字ではなく文字が含まれていることを知る方法はありませんfscanf()
。2 番目もテストする必要がありfscanf()
ます。EOF、または 0 または 1 または 2 (すべて問題を示します) を取得するか、3 回の変換で成功を示す 3 を取得する場合があります。フォーマットに追加\n
すると空白行がスキップされることに注意してください。%d
空白を最初の桁にスキップします。
私が不器用に行ったように、行全体を無視して読むことができる他の方法はありますか?これのためにできる最も近いことfscanf(f,"%*d%*d%*d")
を使用していますか?%*[^\n]
行全体をスキップする最良の方法は、私の回答のコードの最後のバージョンのように、fgets() を使用することです。明らかに、これらの行のいずれかが 4095 バイトより長い場合、行を誤ってカウントする外部の可能性があります。OTOH、それはかなりありそうもないです。
私は今混乱していて、それを質問したくありません. <code>fscanf() は空白を自動的に無視するので、最初の行の後、指定子に従って 3 つの整数が読み取られて無視されると、次の実行で読み取りを開始するときに改行も無視される%*d%*d%*d
と予想されます。fscanf()
ループの。しかし、追加の%*c
またはを使用しても\n
問題が発生せず、コードを使用してもプログラムが正常に動作するのはなぜですか?%*d%*d%*d%*c
%*d%*d%*d\n
これらの形式のどこで問題が発生したかはわかりません。EOF を検出することはできますが、それ以外の場合fscanf()
は 0 を返します。ただし、%*d
先頭の空白 (改行を含む) をスキップするため、3 番目の数値の後の改行を で読むかどうかは問題ではありませ%*c
ん\n
。これは空白であるため、読み取りは改行と末尾または先頭の空白をスキップし、空白以外の文字に到達すると停止します。もちろん、3 つの数字の途中に改行を入れることも、1 行に 3 つ以上の数字を入れることもできます。
ユーザーが端末で入力している場合、形式の末尾\n
は特に奇妙であることに注意してください。ユーザーはリターンを押し、リターンを押し続けますが、ユーザーが空白以外の文字を入力するまでプログラムは続行しません。fscanf()
これが、データが信頼できない場合に を使用するのが非常に難しい理由です。信頼できる場合は簡単ですが、何か問題が発生した場合の診断と回復は苦痛です。fgets()
そのため、andを使用する方が適切sscanf()
です。解析対象を制御でき、必要に応じて別の形式で再試行でき、 fscanf() が解釈できなかったものだけでなく、行全体を報告できます。
%c
(and %*c
) は空白をスキップしないことに注意してください。したがって、%*c
形式の末尾にある a は、読み取られた数値の後の文字を読み取ります (そして破棄します)。それが改行の場合、それは読み取られて無視される文字です。スキャン セット%[...]
は、空白をスキップしないもう 1 つの変換仕様です。他のすべての標準変換指定では、先頭の空白がスキップされます。