C言語関係掲示板

過去ログ

No.405.ファイルの終わりの判定

[戻る] [ホームページ]
No.2803

ファイルの終わり
投稿者---マル(2002/09/29 08:07:00)


ファイルの終わりまでループで繰返しを行いたいのですが、
条件式をどうのように書いたらいいのかわかりません。
どなたか教えてください。

No.2808

Re:ファイルの終わり
投稿者---kamadoma(2002/09/29 14:14:33)


>ファイルの終わりまでループで繰返しを行いたいのですが、
>条件式をどうのように書いたらいいのかわかりません。
>どなたか教えてください。

ファイルの終りを判定するには基本的にはfgetsという関数を用いて
判定します。fgetsの仕様を調べればわかると思いますが一応
サンプルソースを書いておきます。下記のソースがファイル読み込み
の常套手段です。
あとはfeofなんていうファイルの終りを判定する関数もあります。

/*ファイルEOFまで一行ずつ読み込む*/
while(fgets(line,256,fp) != NULL){
/*読み込んだ一行を用いて、何か処理をする*/
}






No.2843

Re:ファイルの終わり
投稿者---かずま(2002/10/03 22:37:33)


> ファイルの終わりまでループで繰返しを行いたいのですが、
> 条件式をどうのように書いたらいいのかわかりません。

使用する入力関数によって異なります。
getc:   1文字読み込み
fgets:  1行読み込み
fread:  指定バイト数読み込み
fscanf: 書式指定読み込み

while ((c = getc(fp)) != EOF) { ... }
while (fgets(buf, sizeof buf, fp) != NULL) { ... }
while (fread(buf, sizeof buf, n, fp) > 0) { ... }
while ((n = fscanf(fp, "%d", &a)) != 1) { ... }

fscanf は、n == EOF の場合、ファイルの終わりですが、
n == 0 の場合、書式指定とは異なる入力があったことになります。

No.2847

Re:ファイルの終わり
投稿者---かずま(2002/10/04 02:08:45)


> while (fread(buf, sizeof buf, n, fp) > 0) { ... }

これはダメですね。次のように訂正します。

struct Data data[10]; という変数があったとして、

while (fread(data, sizeof *data, 10, fp) > 0) { ... }

sizeof *data は、sizeof(*data) と書いても、sizeof(data[0]) と書いても、
sizeof(strudt Data) と書いてもかまいません。
fread は、sizeof(struct Data) * 10 バイトのデータを読み込もうとします。

No.2852

お返事ありがとうございます
投稿者---マル(2002/10/04 20:17:06)


最初は(1)のやり方でやっていたのですが,このやり方だと最後の一回分を余分に繰り返してしまって,処理がうまくいきません。

(1)
while(!feof(fp1)){
fscanf(fp1,"%s\n",a);
fread(&data1,sizeof(struct data),1,fp1);
}

かずまさんが言ってたやり方は,(2)の書き方で合っているのでしょか?
なかなか理解できず,すいません。

(2)
while(fread(&data1,sizeof(struct data),1,fp1)>0){
fscanf(fp1,"%s\n",a);
fread(&data1,sizeof(struct data),1,fp1);
}



No.2866

Re:お返事ありがとうございます
投稿者---かずま(2002/10/06 03:13:45)


>(1)
> while(!feof(fp1)){
> fscanf(fp1,"%s\n",a);
> fread(&data1,sizeof(struct data),1,fp1);
> }

feof() は、これ以前に実行した入力関数(getc, fgets, fread, fscanf など)
で正しくデータを読めなくてその原因が EOF だった場合に、真となります。
これから、実行する入力関数について、EOF にならないことを保証するものでは
ありません。

たとえば、
    while (!feof(fp)) {
        fgets(buf, sizeof buf, fp);
        fputs(buf, stdout);
    }
このプログラムで、5行のファイルを読んだとします。
fgets を 5回実行したあと、ファイルオフセットはファイルの最後になっています。
しかし、5行目は正しく読めたので、fgets は NULL を返さないし、EOF に達したこと
を示すフラグが fp の指す FILE 構造体の中にセットされるわけでもないので、
次の feof(fp) は真になりません。だから、6回目の fgets が実行されてしまい、
それは、EOF を検出し、エラーとなって NULL を返します。ところが、このプログラム
では、fgets の返す値をチェックしていないので、buf には以前の 5行目のデータが
残っているのをそのまま出力してしまいます。


> かずまさんが言ってたやり方は,(2)の書き方で合っているのでしょか?
> なかなか理解できず,すいません。
>
>(2)
> while(fread(&data1,sizeof(struct data),1,fp1)>0){
> fscanf(fp1,"%s\n",a);
> fread(&data1,sizeof(struct data),1,fp1);
> }

(1) は fscanf と fread の組み合わせ、(2) は fread と fscanf と fread の
組み合わせでループを構成していますから、明らかに異なるものです。
また、(2) の scanf と 2つ目の fread は返される値をチェックしていません。

原則として、入力関数は、すべて返却値をチェックすべきです。
入力関数を、複数組み合わせる場合では、少なくとも最後の関数の返す値は
チェックすべきです。そこで、EOF かどうか判定するのです。

あと気になるのは、fscanf の書式の中の "\n" です。
これには特別な意味があって、" " と同じなのをご存知でしょうか。

読み込むファイルの構造はどうなっているのでしょうか。それによって
コード書き方が代わってきます。

 「空白を含まない文字列」+「改行コード」+「構造体データ」

が複数あるということでしょうか。
構造体データがあるということは、バイナリモードでオープンしているんでしょうか。
改行コードは "\r\n" でしょうか。構造体データの先頭に、0x20 や 0x0A といった
バイトデータが来ることはないのでしょうか。

No.2886

Re:お返事ありがとうございます
投稿者---momo(2002/10/08 16:29:47)



 あまり知識がないので質問にそえる答えができませんがファイルの構成として,一レコードが

 「空白を含まない文字列」「構造体のint型のデータ4つ」です。

「空白を含まない文字列」をfscanfで読み込む
「構造体のint型のデータ4つ」をfreadで読み込みます。
必要最低限のメモリでファイルに読み書きしたいので,こんなやり方をしています。

 返却値をチェックするということはどのようなやり方なのでしょうか?
相変わらず,最後のレコードを読み込んでしまいます。
よろしければお返事おねがいします。

No.2889

Re:お返事ありがとうございます
投稿者---かずま(2002/10/08 18:56:20)


> ファイルの構成として,一レコードが
>
> 「空白を含まない文字列」「構造体のint型のデータ4つ」です。
>
>「空白を含まない文字列」をfscanfで読み込む
>「構造体のint型のデータ4つ」をfreadで読み込みます。

fscanf の "%s" は、読み込む文字列の終わりをどうやって判断するか
わかりますか。空白(スペース、タブ、改行など)を見つけて終わりを判断し
ます。その空白文字は、もう一度、入力に押し戻され、次の入力となります。
「空白を含まない文字列」と「構造体のint型のデータ4つ」の間に、そういう
空白文字がないと、文字列を正しく読み込むことが出来ません。

次のプログラムは参考になりますか。
#include <stdio.h>

typedef struct {
    int m1, m2, m3, m4;
} Data;

int main()
{
    static char *name[3] = { "abc", "defgh", "xyz" };
    static Data data[3] = {
        { 1, 2, 3, 4 },
        { 32, 9, 10, 13 },
        { 65, 66, 67, 10 },
    };

    char name2[10][100];
    Data data2[10];
    FILE *fp;
    int i, n;

    fp = fopen("file.dat", "wb");
    if (fp == NULL) return 1;
    for (i = 0; i < 3; i++) {
        fprintf(fp, "%s ", name[i]);    /* "%s\n" でもよい */
        fwrite(&data[i], sizeof(Data), 1, fp);
    }
    fclose(fp);

    fp = fopen("file.dat", "rb");
    if (fp == NULL) return 1;
    for (n = 0; n < 10; n++) {
        if (fscanf(fp, "%s%*c", name2[n]) != 1) break;
        if (fread(&data2[n], sizeof(Data), 1, fp) != 1) break;
    }
    fclose(fp);

    for (i = 0; i < n; i++)
        printf("%s: %d, %d, %d, %d\n", name2[i],
            data2[i].m1, data2[i].m2, data2[i].m3, data2[i]);
    return 0;
}


No.2890

Re:お返事ありがとうございます
投稿者---かずま(2002/10/08 19:00:20)


>        data2[i].m1, data2[i].m2, data2[i].m3, data2[i]);

訂正です。
        data2[i].m1, data2[i].m2, data2[i].m3, data2[i].m4);