1

%dここで使用される場合、フォーマット指定子fscanf()は整数を読み取り、改行を含むその前の空白を無視することを知っています(私はそれを確認しました).しかし、 fscanf()3で複数行のファイルから読み取るために使用する次のプログラムではそれぞれ整数、フォーマット文字列"%d%d%d%*c"は と同じように機能し"%d%d%d"ます。

なぜそうなのですか?フォーマット指定子文字列の最初のフォーマット指定子としてfscanf()使用すると、整数の前にある空白が無視されるため、最後の指定子として使用される余分な文字列がエラーや副作用を引き起こさないのはなぜですか?指定子が行内の3つの数字の各グループの後に改行を追加すると、改行が食い尽くされるので理にかなっています.しかし、デフォルトで空白を無視しても、エラーや副作用なしで機能するのはなぜですか? %*c が食べる文字を見つけられず、指定子と入力の間に不一致がある場合、 fscanf() はスキャンを停止すべきではありませんか? 同じように、不一致があるときに停止することはありませんか?%d%*c%d%*cfscanf()%dfscanf()scanf()

編集:使用しても機能します"%*c%d%d%d"!! フォーマット指定子と最初の入力が一致しないと、後続の文字のスキャンと処理を停止するべきではありませんか?

#include <stdio.h>
#include <stdlib.h>


int main ()
{
int n1,n2,n3;
FILE *fp;
fp=fopen("D:\\data.txt","r");

if(fp==NULL)
{
printf("Error");
exit(-1);
}

while(fscanf(fp,"%d%d%d%*c",&n1,&n2,&n3)!=EOF) //Works as good as line below
//while(fscanf(fp,"%d%d%d",&n1,&n2,&n3)!=EOF)
printf("%d,%d,%d\n",n1,n2,n3);
fclose(fp);

}

ファイル内のデータの形式は次のdata.txtとおりです。

243 343 434
393 322 439
984 143 943
438 243 938

出力:

243 343 434
393 322 439
984 143 943
438 243 938
4

2 に答える 2

3

問題のプログラムのこのバリエーションを検討してください。

#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 つの変換仕様です。他のすべての標準変換指定では、先頭の空白がスキップされます。

于 2013-05-15T06:39:14.573 に答える
1

fscanf() が成功すると、この関数は、正常に埋められた引数リストの項目数を返します。このカウントは、一致の失敗、読み取りエラー、またはファイルの終わりに達したために、予想される項目数と一致するか、それより少ない (0 の場合もある) 可能性があります。

上記の段落では、不一致で停止することについては決して話しません。追加の指定子も試行し、入力がないため、正常にスキャンされた数値のみが返されます。フォーマット仕様の引数が多すぎる場合、余分な引数は無視されます。フォーマット指定に十分な引数がない場合、結果は未定義です。

于 2013-05-15T06:30:41.040 に答える