バイナリ保存
構造体をそのままの形で保存する
入門のファイル入出力では、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本のプログラムで確かめます。
要件
- fwrite で Student をそのまま書き出し、3件書き込みました の形で書けた件数を出す
- 同じファイルを開き直し、fread で読み戻して 3件読み戻しました の形で読めた件数を出す
- 読み戻した内容を 佐藤 80点 の形で1件ずつ出す
- 開くモードは書き込みが wb、読み込みが rb にする
入出力例
main("3
佐藤 80
鈴木 92
高橋 65
") → "3件書き込みました
3件読み戻しました
佐藤 80点
鈴木 92点
高橋 65点"
main("2
田中 74
渡辺 88
") → "2件書き込みました
2件読み戻しました
田中 74点
渡辺 88点"