ファイル入出力

ファイル操作

これまでの知識では、外部のデータを読み書きすることが不可能だった。つまり、プログラムの終了時に内部データが失われてしまうため、ランキングやハイスコアを実装できない。また、マップデータや敵のパラメータなどをソースファイル内に記述すると、調整のたびにいちいちコンパイルを通さなければならず、非常に面倒である。
ファイル操作を学ぶことで、これらの問題点を解消できる。ぜひ覚えよう。

ファイルを扱うためには、まず、FILE型へのポインタを用意し、fopen()でファイルを紐付けする。すると、プログラム中でそのファイルを触ることが可能になる。
また、開いたファイルはfclose()で明示的に閉じる必要がある。

#include <stdio.h>

int main()
{
    const char* filename = "noclose.txt";
    FILE* fp;

    for (int i = 0; i < 600; ++i)
    {
        fp = fopen(filename, "w");
        printf("%d\n", fp->_file);
        //fclose(fp); //閉じ忘れると……?
    }

    return 0;
}

ファイルを開くとき、モードを指定する必要がある。以下の文字列を第2引数に指定する。

  • "r" テキスト読み取り
  • "w" テキスト生成(上書き)
  • "a" テキスト追加(終端から書き込み)
  • "rb" バイナリ読み取り
  • "wb" バイナリ生成(上書き)
  • "ab" バイナリ追加(終端から書き込み)
  • "r+" テキスト更新
  • "w+" テキスト生成/読み取り(上書き)
  • "a+" テキスト更新(終端から書き込み)
  • "r+b"/"rb+" バイナリ更新
  • "w+b"/"wb+" バイナリ生成/読み取り(上書き)
  • "a+b"/"ab+" バイナリ更新(終端から書き込み)

テキストとバイナリ

テキストとは、文字列でデータを保持するファイル形式である。int型の変数のサイズは桁数*sizeof(char)である。
バイナリとは、ビット列でデータを保持するファイル形式である。int型の変数のサイズはsizeof(int)である。
実際に使う上では、以下の様な特徴があるだろう。

テキスト

  • テキストエディタがあれば編集が可能で、直感的。
  • プログラム中で文字列←→データに相互移動する必要がある。

バイナリ

  • エディタを作らなければ編集が大変。
  • プログラム中では、データを直接読み書きできる。

テキスト形式

テキストファイルについては、これまで扱ってきたprintf、scanfなどに対応するような関数が存在する。
printf(文字列, ...)→fprintf(ファイルポインタ, 文字列, ...)
scanf(文字列, ...)→fscanf(ファイルポインタ, 文字列, ...)
getchar()→fgetc(ファイルポインタ)
rewind(ファイルポインタ):ファイルポインタをファイルの先頭まで巻き戻す

//前回実行時の文字列を表示するプログラム
#include <stdio.h>

int main()
{
    char str[256];
    const char* filename = "text.txt";
    FILE* fp = fopen(filename, "r");

    if (fp == NULL)
    {
        printf("前回実行時の結果はありません\n");
    }
    else
    {
        fscanf(fp, "%255s", str);
        fclose(fp);
        printf("前回実行時:\n%s\n", str);
    }

    printf("文字列を入力してください\n");
    scanf("%255s", str);

    fp = fopen(filename, "w");
    fprintf(fp, "%s", str);
    fclose(fp);

    return 0;
}

バイナリ形式

バイナリファイルは、fwrite、freadで読み書きできる。
fwrite(ポインタ、サイズ、要素数、ファイルポインタ)
fread(ポインタ、サイズ、要素数、ファイルポインタ)
第1引数がポインタであることから分かるように、配列を読み書きしやすいよう設計されている。

//2,3,5の平方根を表示するプログラム
#include <stdio.h>
#include <math.h>

int main()
{
    const char* filename = "bin.b";
    double arr[3] = { sqrt(2.0), sqrt(3.0), sqrt(5.0) };

    FILE* fp;

    fp = fopen(filename, "rb");
    if (fp != NULL)
    {
        fread(arr, sizeof(double), 3, fp);
        fclose(fp);

        for (int i = 0; i < 3; ++i)
        {
            printf("%.16d\n", arr[i]);
        }
    }
    else
    {
        fp = fopen(filename, "wb");
        fwrite(arr, sizeof(double), 3, fp);
        fclose(fp);
    }

    return 0;
}

課題

  1. 5位まで保存できるランキングを実装せよ。データの構造体を以下に示す。
    • scanfで構造体にデータを渡し、ソートして保存する。
    • ファイルからデータを読み込み、出力する。
    • 保存形式は問わない。
typedef struct
{
    char name[64];
    int score;
}rankdata;

最終課題

ゲームを制作せよ。