バイナリ保存

構造体をそのままの形で保存する

入門のファイル入出力では、fprintf で数値を文字に直して書き、fscanf で文字を数値に戻して読みました。人が中身を読めるのは大きな利点ですが、書くたびに変換が入り、構造体の項目が増えるほど書式の指定が長くなります。

もう1つのやり方が、メモリにある並びをそのまま書き出す バイナリ保存 です。

FILE *fp = fopen("students.bin", "wb"); fwrite(list, sizeof(Student), n, fp); fclose(fp);

fwrite に渡すのは、先頭のアドレス、1個あたりの大きさ、個数、ファイルの4つです。戻り値は 書けた個数 で、頼んだ個数と違えば途中で失敗しています。読むときは同じ形の fread を使い、こちらの戻り値は 読めた個数 になります。

FILE *fp = fopen("students.bin", "rb"); int got = (int)fread(loaded, sizeof(Student), n, fp);

"wb""rb"b はバイナリの意味です。Linux や macOS では付けても付けなくても同じ動きですが、Windows では改行にあたるバイトが勝手に書き換えられてデータが壊れます。付ける習慣にしておくのが安全です。

何がうれしくて、何が危ないか

変換が無いぶん速く、名前に空白が入っていても fscanf のように途中で切れません。1件の大きさが決まっているので、5件目だけを読みたいときに先頭から順に読み直さず、いきなりその位置へ飛べるのも利点です。

一方で、書き出したファイルは人には読めず、テキストエディタで直せません。さらに 別の環境で読めるとは限りません。int の大きさ、バイトを並べる向き、構造体に入る詰め物の量は処理系によって変わります。同じプログラムが作って同じプログラムが読む、という前提のときだけ使える形式だと考えてください。長く保存したいものや、他のプログラムに渡すものは、テキストや決まった形式のほうが安全です。

ポインタを含む構造体は保存できない

保存してはいけないものが1つあります。ポインタを持つ構造体 です。Node *next を書き出しても、ファイルに残るのはそのときの番地の数値だけで、次に起動したときには何も指していません。読み戻したあとにたどると、まったく無関係な場所へ飛びます。

第5章の連結リストをそのまま fwrite できないのはこのためです。リストを保存するときは、中身だけを1件ずつ書き出し、読むときに改めてつなぎ直します。

このレッスンでは、書いてすぐ読み戻すところまでを1本のプログラムで確かめます。

要件

  1. fwrite で Student をそのまま書き出し、3件書き込みました の形で書けた件数を出す
  2. 同じファイルを開き直し、fread で読み戻して 3件読み戻しました の形で読めた件数を出す
  3. 読み戻した内容を 佐藤 80点 の形で1件ずつ出す
  4. 開くモードは書き込みが wb、読み込みが rb にする

入出力例

main("3 佐藤 80 鈴木 92 高橋 65 ") → "3件書き込みました 3件読み戻しました 佐藤 80鈴木 92高橋 65点" main("2 田中 74 渡辺 88 ") → "2件書き込みました 2件読み戻しました 田中 74渡辺 88点"

ヒント

生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア
編集 LuaGate編集部
main.c
学習モード

メモ

バイナリ保存

⌘S で保存