これまでデータの個数が分かった上でデータの読み込みを行ってきましたが、プログラムによってはデータの個数が不明なケースがあります。
例えば、データが10個以内ということだけ分かっていて、実際には何個あるか分からないというケースを想定してみます。
まずはデータファイルを作ってみます。
カレントフォルダに「Data.txt」を作成し、下記のデータを打ち込んで保存してください。
見ての通り、5つのデータが入っています。
※50の後ろは改行していません!
このデータを配列に読み込むプログラムを作ります。
まずは枠組みから作りましょう。
<sample program 116-01>
#include <stdio.h> #define DATA_MAX 10 int main(void) { int data[DATA_MAX]; FILE* fp; fp = fopen("Data.txt", "r"); if (fp == NULL) { printf("OPEN ERROR\n"); return 1; } /* データ読み込み */ fclose(fp); return 0; } |
ここにデータの読み込みと表示を追加します。
ただ、データは10個以内ということは分かっていますが、実際の個数は分からないと仮定します。
データの個数が分からないので、10回ループさせてデータを読み込んでみます。
<sample program 116-02>
#include <stdio.h> #define DATA_MAX 10 int main(void) { int data[DATA_MAX]; int i; FILE* fp; fp = fopen("Data.txt", "r"); if (fp == NULL) { printf("OPEN ERROR\n"); return 1; } /* データ読み込み */ for (i = 0; i < DATA_MAX; i++) { fscanf(fp, "%d", &data[i]); } fclose(fp); /* データ表示 */ for (i = 0; i < DATA_MAX; i++) { printf("data[%d] = %d\n", i, data[i]); } return 0; } |
<実行結果>
data[0] = 10 data[1] = 20 data[2] = 30 data[3] = 40 data[4] = 50 data[5] = -858993460 data[6] = -858993460 data[7] = -858993460 data[8] = -858993460 data[9] = -858993460 続行するには何かキーを押してください・・・
データは5個分だけ読み込まれ、それ以降は不明なデータが入っています。
※Visual Studioの実行結果です。他のコンパイラでは異なる結果が出る可能性があります。
この不明なデータは読み込まれたのでしょうか?それとも、最初から入っていたのでしょうか?
配列に初期値を入れて確かめてみましょう。
<sample program 116-03>
#include <stdio.h>
#define DATA_MAX 10
int main(void)
{
int data[DATA_MAX];
int i;
FILE* fp;
fp = fopen("Data.txt", "r");
if (fp == NULL) {
printf("OPEN ERROR\n");
return 1;
}
/* 配列を0で初期化 */
for (i = 0; i < DATA_MAX; i++) {
data[i] = 0;
}
/* データ読み込み */
for (i = 0; i < DATA_MAX; i++) {
fscanf(fp, "%d", &data[i]);
}
fclose(fp);
/* データ表示 */
for (i = 0; i < DATA_MAX; i++) {
printf("data[%d] = %d\n", i, data[i]);
}
return 0;
}
|
<実行結果>
data[0] = 10 data[1] = 20 data[2] = 30 data[3] = 40 data[4] = 50 data[5] = 0 data[6] = 0 data[7] = 0 data[8] = 0 data[9] = 0 続行するには何かキーを押してください・・・
初期化した0が入っているということは、6個目以降のデータは読み込まれていないということです。
※Visual Studioの実行結果です。他のコンパイラでは異なる結果が出る可能性があります。
これで終わりではありません。
データが無いのに読み込み続けるのは無駄な気がします。
最大1000個あるデータで、実際には5個しかデータが無ければ995回分の読み込みは無駄になります・・・
そのような時に使えるのが EOF( End Of File )です。
ファイル内のデータの終わりには特殊なEOFという値が入っており、それをチェックする関数があります。
EOFを検出するfeof関数の使い方を説明します。
<sample program 116-04>
#include <stdio.h> #define DATA_MAX 10 int main(void) { int data[DATA_MAX]; int i; int data_num; FILE* fp; fp = fopen("Data.txt", "r"); if (fp == NULL) { printf("OPEN ERROR\n"); return 1; } /* データ読み込み */ data_num = 0; while (!feof(fp)) { fscanf(fp, "%d", &data[data_num]); data_num++; } fclose(fp); /* データ表示 */ printf("データの個数 %d個\n", data_num); for (i = 0; i < data_num; i++) { printf("data[%d] = %d\n", i, data[i]); } return 0; } |
<実行結果>
データの個数 5個 data[0] = 10 data[1] = 20 data[2] = 30 data[3] = 40 data[4] = 50 続行するには何かキーを押してください・・・
繰り返し回数が分からないため、while文を使って繰り返しを作りました。
繰り返し条件は、feof関数の戻り値を使っています。
feof関数は、ファイルの終わりで無ければ「0」を、ファイルの終わりに達していたら「0以外」を返します。
if文で説明しましたが、「0」は不成立、「0以外」は成立を表します。
while文は「条件が成り立っている間繰り返す」命令ですから、
while (feof(fp)) { |
と書くと、「ファイルの終わりに達している間繰り返す」になり、1個目のデータの時点で条件が成立しません。
while (!feof(fp)) { |
のように、「!」を付け条件を否定することで、「ファイルの終わりに達していない間繰り返す」という形にしました。
さらに、data_numという変数を用意し、配列dataの添え字として使っています。
このdata_numはそのままデータの個数を数えるカウンタとして機能します。
ですので、データを表示する際にも無駄なく表示することが出来ています。
上手くいったように見えますが、注意点があります。
「Data.txt」を開いて50の後ろに改行を入れてみてください。
この状態でプログラムを実行します。
<実行結果>
データの個数 6個 data[0] = 10 data[1] = 20 data[2] = 30 data[3] = 40 data[4] = 50 data[5] = -858993460 続行するには何かキーを押してください・・・
50の後ろに改行が入ることで、EOFの前に「区切り文字」が入ったため、1回多く読み込みを行ってしまいました。
※Visual Studioの実行結果です。他のコンパイラでは異なる結果が出る可能性があります。
データの作り方によっても結果が違って来るということを覚えておいてください。