導入
これまでの fprintf・fgets は、人間が読める「テキスト」としてファイルに保存する方法でした。しかし、構造体や配列の値を、変換せずメモリにある並びのまま高速に保存したい場合は、バイナリ用の fwrite・fread を使います。
説明
バイナリファイルを扱うときは、モードに b を付けて "wb"(書き込み)・"rb"(読み込み)のように開くのが習慣です。
fwrite(データの先頭アドレス, 1個のサイズ, 個数, fp);… 指定した個数分のデータをそのままバイト列として書き込みます。fread(受け取り先の先頭アドレス, 1個のサイズ, 個数, fp);… 同じ形でファイルから読み込み、実際に読み込めた個数を返します。
sizeof(型) と組み合わせれば、構造体を1つのまとまり(ブロック)としてまるごと書き込み・読み込みできます。
#include <stdio.h>
struct Player {
char name[16];
int score;
};
int main(void) {
struct Player p1 = {"Taro", 85};
struct Player p2 = {"Hanako", 92};
FILE *fp = fopen("/home/user/players.bin", "wb");
if (fp == NULL) {
printf("書き込み用に開けませんでした\n");
return 1;
}
fwrite(&p1, sizeof(struct Player), 1, fp);
fwrite(&p2, sizeof(struct Player), 1, fp);
fclose(fp);
printf("書き込み完了(構造体%zuバイト x 2件)\n", sizeof(struct Player));
fp = fopen("/home/user/players.bin", "rb");
if (fp == NULL) {
printf("読み込み用に開けませんでした\n");
return 1;
}
struct Player buf;
while (fread(&buf, sizeof(struct Player), 1, fp) == 1) {
printf("%s: %d点\n", buf.name, buf.score);
}
fclose(fp);
return 0;
}
構造体だけでなく、配列もまとめて1回の fwrite/fread で保存・復元できます。
#include <stdio.h>
int main(void) {
int nums[5] = {10, 20, 30, 40, 50};
FILE *fp = fopen("/home/user/nums.bin", "wb");
fwrite(nums, sizeof(int), 5, fp);
fclose(fp);
int loaded[5];
fp = fopen("/home/user/nums.bin", "rb");
fread(loaded, sizeof(int), 5, fp);
fclose(fp);
for (int i = 0; i < 5; i++) {
printf("%d ", loaded[i]);
}
printf("\n");
return 0;
}
やってみよう
players.bin の例に3人目の struct Player を追加してみましょう。また printf("...%zu...", sizeof(struct Player)); の結果を確かめて、char name[16] と int score を合わせたバイト数と一致しているか見てみてください。
演習
name と score を持つ struct Player の値を1件作り、fwrite でファイルに書き込んで、fread で読み込んで表示するプログラムを書いてください。
ヒント1を見る
struct Player p = {"Jiro", 78}; を作り、fwrite(&p, sizeof(struct Player), 1, fp); で書き込みます。
ヒント2を見る
読み込みは struct Player buf; fread(&buf, sizeof(struct Player), 1, fp); として、buf.name と buf.score を表示します。