本文へスキップ
BecomeCoder

C言語コース · 第11章 ファイル入出力 · レッスン45

fread/fwrite ― バイナリファイルの読み書き

ブラウザで完結

導入

これまでの fprintffgets は、人間が読める「テキスト」としてファイルに保存する方法でした。しかし、構造体や配列の値を、変換せずメモリにある並びのまま高速に保存したい場合は、バイナリ用の fwritefread を使います。

説明

バイナリファイルを扱うときは、モードに 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回の fwritefread で保存・復元できます。

#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 を合わせたバイト数と一致しているか見てみてください。

演習

namescore を持つ 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.namebuf.score を表示します。

実際に動かしてみよう

下のエディタにCを書いて「コンパイル & 実行」を押すと、ブラウザ内で本物のCコンパイラ(clang + dlmalloc)がその場でコンパイルして実行し、出力が表示されます。構造体・共用体・malloc/free・関数ポインタ・ファイル入出力などがそのまま動きます。本文の例を書き換えて試しながら進めましょう(初回だけコンパイラの読み込みに時間がかかります。以降はキャッシュされます。この回は標準入力(scanf)は使わず、値はコードに直接書きます)。

C/C++ — ブラウザ内で本物のコンパイラで実行

ブラウザ内で本物のC/C++コンパイラ(clang + libc++ / WebAssembly)を動かします。構造体・malloc/free・std::string・vector・クラス・テンプレート・STL などがそのまま実行できます(初回のみコンパイラの読み込みに時間がかかります。以降はブラウザにキャッシュされます)。
スクロールして表示された時点でも自動で読み込まれます。