今回はバイナリファイルの特徴を生かした使い方を説明します。
まずバイナリファイルの特性とは何かから説明します。
テキストファイルはデータを文字として保存します。
例えば数値の123は文字の"123"としてファイルに保存します。
バイナリファイルはデータを2進数でバイト単位に保存します。
同じく数値の123は 00000000 00000000 00000000 01111011 というビットデータで保存されます。
テキストファイルで12345を保存すると"12345"として保存します。
123と比べて2桁増えています。1文字は8ビット(1バイト)ですので、桁が増えると保存するデータ量が増えていきます。
バイナリファイルで12345を保存すると 00000000 00000000 00110000 00111001 です。
長く見えますが123の時とバイト数は変わっていません。
テキストファイルは1個のデータのサイズが変わりますが、バイナリファイルは変わらないというところがポイントです。
下のプログラムを動かし、バイナリファイルを作成します。
5人分の名前と年齢のデータが構造体配列に入っています。
これを「People.ppl」という独自に作った拡張子のファイルに保存します。
※「txt」では無くなるため、ダブルクリックでメモ帳は開かなくなります。
<sample program 125-01>
#include <stdio.h>
#define PERSON_MAX 5
#define NAME_MAX 21
typedef struct{
char name[NAME_MAX];
int age;
} Person;
int main(void)
{
Person people[PERSON_MAX] = {
{ "Alex", 21 },
{ "Billy", 25 },
{ "Charlie", 30},
{ "Dennis", 42 },
{ "Eddy", 48 }
};
FILE* fp;
fp = fopen("People.ppl", "wb");
if (fp == NULL) {
printf("OPEN ERROR\n");
return 1;
}
fwrite(people, sizeof(Person), PERSON_MAX, fp);
fclose(fp);
return 0;
}
|
実行して、「People.ppl」を作成します。
※中身が見たい方は、先にメモ帳を開いておき、「People.ppl」をドラッグドロップしてください。
次に読み込みのプログラムを作るのですが、今回やりたいことを書きます。
全てのデータを読み込むのではなく、こちらで指定したデータを1つだけ読み込みたいです。
まずは枠組みを作りましょう。
<sample program 125-02>
#include <stdio.h>
#define PERSON_MAX 5
#define NAME_MAX 21
typedef struct{
char name[NAME_MAX];
int age;
} Person;
int main(void)
{
Person person;
FILE* fp;
fp = fopen("People.ppl", "rb");
if (fp == NULL) {
printf("OPEN ERROR\n");
return 1;
}
fclose(fp);
return 0;
}
|
まだ読み込み部分は入れていません。
これまでのプログラムで、ファイルは先頭から読み込まれることを見てきたと思います。
テキストファイルでは区切り文字があり、読み込み用の関数が呼ばれる度にファイルに入っているデータが順次読み込まれました。
バイナリファイルでは区切り文字はありませんが、バイト数を区切って順番に読み込むことも出来ます。
ファイルの中には、次に読み書きするデータの位置を保持している場所があり、データを読み込む度に自動的に次のデータを指していきます。
その「読み込むデータの位置」を変える関数が「fseek関数」です。
<fseek関数>
fseek(@, A, B); @ファイルポインタ A基準位置からのバイト数 B基準位置 基準位置 SEEK_SET ファイルの先頭 SEEK_CUR 現在の位置 SEEK_END ファイルの終わり |
この関数を使えば、読み込む前に位置を変更することが出来ます。
取りあえず、2人目のデータを読み込むプログラムを追加してみます。
<sample program 125-03>
#include <stdio.h>
#define PERSON_MAX 5
#define NAME_MAX 21
typedef struct{
char name[NAME_MAX];
int age;
} Person;
int main(void)
{
Person person;
FILE* fp;
fp = fopen("People.ppl", "rb");
if (fp == NULL) {
printf("OPEN ERROR\n");
return 1;
}
fseek(fp, sizeof(Person), SEEK_SET);
fread(&person, sizeof(Person), 1, fp);
fclose(fp);
printf("Name = %s\n", person.name);
printf("Age = %d\n", person.age);
return 0;
}
|
<実行結果>
Name = Billy Age = 25 続行するには何かキーを押してください・・・
1人目のAlexは読まれず、2人目のBillyのデータが読み込まれました。
SEEK_SETがファイルの先頭を表しますから、先頭から1人分(sizeof(Person))後へ読み込み位置を変更しました。
冒頭で書きましたが、格納したデータによって1人分のデータサイズが変わることはありませんので、それぞれのデータの先頭番地に移動させることが出来るのです。
それでは、指定した番号(0番から4番)のデータを読み込めるよう変更してみましょう。
<sample program 125-04>
#include <stdio.h>
#define PERSON_MAX 5
#define NAME_MAX 21
typedef struct{
char name[NAME_MAX];
int age;
} Person;
int main(void)
{
Person person;
int input;
FILE* fp;
fp = fopen("People.ppl", "rb");
if (fp == NULL) {
printf("OPEN ERROR\n");
return 1;
}
do {
scanf("%d", &input);
} while (input < 0 || input >= PERSON_MAX);
fseek(fp, sizeof(Person) * input, SEEK_SET);
fread(&person, sizeof(Person), 1, fp);
fclose(fp);
printf("Name = %s\n", person.name);
printf("Age = %d\n", person.age);
return 0;
}
|
<実行結果>
3 Name = Dennis Age = 42 続行するには何かキーを押してください・・・
ファイルを開いたまま、必要な時に必要なデータを読み込むことも可能です。
工夫すれば色々な場面で使えそうですね。