C言語入門|char配列をバイナリとして読み書きする方法

これまで、fputs や fgets を使った 文字列としてのファイル入出力 を学んできました。
しかし、C言語のファイル入出力にはもう一つ重要な考え方があります。それが バイナリ入出力 です。

バイナリ入出力では、データを「文字」としてではなく、決められたサイズのバイト列として扱います。
そのため、文字列でおなじみの ヌル文字 '\0' は特別扱いされず、読み書きの終了は サイズ指定 によって決まります。

この記事では、
char配列を文字列ではなく「バイナリデータ」として読み書きする方法 を、サンプルプログラムを通して解説します。

char配列をバイナリとして読み書きする

次のプログラムは、

  1. char配列の先頭10バイトを 一括で書き込み
  2. その後、1バイトずつ 10個読み取る
  3. 読み取ったデータを文字列として表示する。

という処理を行います。

サンプルプログラム

プロジェクト名:14-10-1 ソースファイル名: sample14-10-1.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    FILE* fp;
    char wbuf[] = "0123456789";   // 書き込み用データ(10バイト)
    char rbuf[16];               // 読み取り用バッファ

    /* --- バイナリ書き込み --- */
    if ((fp = fopen("try.dat", "wb")) == NULL) {
        printf("ファイルを開けませんでした\n");
        exit(1);
    }

    fwrite(wbuf, 10, 1, fp);     // 10バイト × 1個を書き込む
    fclose(fp);

    /* --- バイナリ読み取り --- */
    if ((fp = fopen("try.dat", "rb")) == NULL) {
        printf("ファイルを開けませんでした\n");
        exit(1);
    }

    int cn = fread(rbuf, 1, 10, fp);  // 1バイト × 10個を読み取る
    rbuf[10] = '\0';                  // 文字列終端を手動で追加
    fclose(fp);

    printf("%d個のデータを読み取りました:%s\n", cn, rbuf);

    return 0;
}

実行結果

10個のデータを読み取りました:0123456789

fwrite による char配列の書き込み

書き込みは、次の1行で行っています。

fwrite(wbuf, 10, 1, fp);

この呼び出しの意味を整理すると次のとおりです。

引数内容
第1引数書き込むデータの先頭アドレス
第2引数データ1個あたりのサイズ(10バイト)
第3引数書き込む個数(1個)
第4引数ファイルポインタ

つまり、

wbufの先頭から10バイト分のデータを、そのままファイルに書き込む

という動作になります。

ここでは文字列かどうかは一切関係ありません。
'\0' が含まれていても、含まれていなくても、指定したサイズ分だけが書き込まれます。

fread による char配列の読み取り

読み取りは次の行で行っています。

int cn = fread(rbuf, 1, 10, fp);

こちらは、

  • 1バイトのデータを
  • 10個分

読み取る指定です。

結果として、10バイト分のデータが rbuf に格納され、
実際に読み取れた個数が戻り値 cn に返されます。

ヌル文字を自分で付ける理由

fread は 文字列を扱う関数ではありません
そのため、読み取ったデータの末尾に '\0' を自動的に付けてくれません。

そこで、次の処理が必要になります。

rbuf[10] = '\0';

これを忘れると、

  • printfで余計な文字が表示される。
  • メモリの外まで読み続ける。

といった不具合につながります。

fgets や fputs との考え方の違い

ここで、文字列入出力との違いを整理しておきましょう。

関数終端の判断方法
fputs / fgets'\0' や改行
fputc / fgetcEOF
fwrite / freadサイズ

fwrite と fread は、
文字文化ではなく、サイズ文化の関数だと考えると理解しやすくなります。

まとめ

  • char配列は 文字列としても、バイト列としても扱える
  • fwrite / fread は サイズ指定で動作する
  • '\0' は自動で処理されないため、自分で付加する必要がある。
  • バイナリ入出力は、セーブデータや高速なデータ保存に必須

この章を理解できれば、
C言語のファイル操作は 「実用段階」 に一気に近づきます。