digirakuda ようこそ!  コンピュータの世界へ!

第16回 バイナリーファイルのリ-ド/ライト

このドキュメントは http://icrus.org/c_language_beginers_course/ 上にあります.

15章では,テキストファイルのリード/ライトを行いました.テキストファイルは,テキストエディタで読めるなど,扱いやすいところがたくさんあり人間との相性は良いようです.しかし,ファイル容量が大きくなる,ファイル容量が大きくなるとリード/ライトに時間がかかる(データが大量になったとき顕著になります)など,短所も多くあります.本格的にファイルを扱うには,コンピュータと相性がいいバイナリーファイルのリード/ライトが必要です.そこでこの章では,バイナリーファイルのリード/ライトについて説明します.

[課題16-1]誕生日のリストをファイルにしよう.(バイナリーファイル)

[プログラム例16-1]


01: #include <stdio.h>
02: #include <stdlib.h>
03: #include <process.h>
04: #define  FILENAME "a:\\binfile.dat"
05: struct borth_day_list{
06:     char name[20];
07:     int  year;
08:     int  month;
09:     int  day;
10: };
11: struct borth_day_list list;
12:
13: void main(void){
14:     FILE *fp;
15:     char str[50];
16:     if( (fp=fopen( FILENAME, "ab" ))==NULL ){
17:         printf( "file open error:%s\n", FILENAME );
18:         exit(1);
19:     }
20:
21:     while(1){
22:         printf("名前を入力して下さい(リターンのみで終了します.):\n");
23:         gets( list.name );
24:         if( list.name[0]=='\0' )
25:             break;
26:         printf("%sさんの生年月日を入力して下さい(例1995,9,30):\n",list.name );
27:         gets( str );
28:         while( sscanf( str, "%d,%d,%d", &list.year, &list.month, &list.day )!=3 ){
29:             printf("%sさんの生年月日をもう一度入力して下さい(例1995,9,30):\n", list.name );
30:             gets( str );
31:         }
32:         printf( "%s,%s-->ファイル\n", list.name, str );
33:         if( fwrite( &list,sizeof(list), 1, fp )<1 ){
34:             printf( "%s Write error", FILENAME );
35:             exit(1);
36:         }
37:     }
38:     if( fclose( fp )==EOF ){
39:         printf( "file close error:%s\n", FILENAME );
40:         exit(1);
41:     }
42: }


05~10行→構造体struct borth_day_listを定義します.
16~19行→ファイルをオープンするときの決まり文句です.バイナリーファイルを追加でオープンしますから,ファイル名("a:\binfile.dat")とオープンモード("ab")でオープンします.
28~31行→間違った誕生日の入力の仕方をすると再入力を要求します.
33~36行目→関数fwrite()を使って変数list(型は構造体borth_day_list)をファイルポインタfpの指すファイルへ出力します.

[結果16-1]

コンパイル・実行すると次のよう表示されます.
名前を入力して下さい(リターンのみで終了します.):
常盤 貴子[リターン印]
常盤 貴子さんの生年月日を入力して下さい(例1995,9,30):
1972,4,30[リターン印]
常盤 貴子,1972,4,30-->ファイル
名前を入力して下さい(リターンのみで終了します.):
森田 和義[リターン印]
森田 和義さんの生年月日を入力して下さい(例1995,9,30):
1945,8[リターン印]
森田 和義さんの生年月日をもう一度入力して下さい(例1995,9,30):
1945,8,22[リターン印]
森田 和義,1945,8,22-->ファイル
名前を入力して下さい(リターンのみで終了します.):
[リターン印]

MS-DOSのTYPEコマンドではバイナリーファイルは見れません.バイナリーファイルを見るためのコマンドDUMPを使って作成したファイルの様子を見てみましょう.
A:\>DUMP A:\BINFILE.DAT

Dump Version 3.10

00000000 8F ED 94 D5 81 40 8B 4D-8E 71 00 00 00 00 00 00 常盤 貴子......
00000010 00 00 00 00 B4 07 04 00-1E 00 90 58 93 63 81 40 ....エ.....森田
00000020 98 61 8B 60 00 00 00 00-00 00 00 00 00 00 99 07 和義............
00000030 08 00 16 00 ....

 次に,同じこのプログラムを実行すると,データをつけ足していくことができます.

[課題16-2] 誕生日のリストからその人が何座か調べよう.(バイナリーファイル用)

[プログラム例16-2]


01: #include <stdio.h>
02: #include <stdlib.h>
03: #include <process.h>
04: #define  FILENAME "a:\\binfile.dat"
05: struct borth_day_list{
06:     char name[20];
07:     int  year;
08:     int  month;
09:     int  day;
10: };
11: struct borth_day_list list;
12:
13: char *seiza[]={ "月のデータがまちがい",
14:    "山羊座", "水瓶座", "魚座",
15:    "牡羊座", "牡牛座", "双子座",
16:    "蟹座", "獅子座", "乙女座",
17:    "天秤座", "蠍  座", "射手座" };
18: void main(void){
19:     FILE *fp;
20:     int get_seiza(int, int);
21:     if( (fp=fopen( FILENAME, "rb" ))==NULL ){
22:         printf( "file open error:%s\n",FILENAME );
23:         exit(1);
24:     }
25:     while( fread(&list,sizeof(list),1,fp)==1 ){
26:         printf( "%-14sさん%2d年%2d月%2d日生まれ\n", list.name, list.year, list.month, list.day );
27:         printf( "%sです.\n", *( seiza + get_seiza( list.month, list.day ) ) );
28:     }
29:     if( fclose( fp )==EOF ){
30:         printf( "file close error:%s\n", FILENAME );
31:         exit(1);
32:     }
33:     getchar();
34: }
35:
36: int get_seiza( int month, int day ){
37:     ~以下関数get_seiza()はプログラム例15-2と同一です~
38: }


05~10行→構造体struct borth_day_listを定義します.
21~24行→ファイルをオープンするときの決まり文句です.今回はデータファイルを読み出しますのでオープンモード"rb"にします.
25~28行ファイルが終了するまで繰り返します.
28行目→関数fread()を使ってファイルのデータを読み出します.listを1つずつ読み込み,読み込んだ個数を返します.ファイルの終わりに来ると戻り値として1を返せなくなりますので1以外の戻り値のときファイルの終わりとみなして,whileループを終わります.
26~27行→関数fread()は,そのまま変数list(型は構造体borth_day_list)を読み込めますので,そのメンバを用いて画面表示をさせます.
29~32行目→ファイルを閉じるときの決まり文句です.

[結果16-2]

コンパイル・実行すると次のよう表示されます.

常盤 貴子 さん 1972年 4月30日生まれ
牡牛座です.
森田 和義 さん 1945年 8月22日生まれ
獅子座です.

リターンキーを押すとプログラムは終了します.
 プログラムは,プログラム例16-1を使って入力したデータファイルを読み出して,その誕生日から星座名を出力するものです.ファイルが終了するまでファイルのデータと星座名を出力します.

[文法16]

1.構造体

 基本的な構造体の定義の仕方について説明します.
struct tag{
type member_name_1;
type member_name_2;
:
type member_name_n;
};
struct tagという名前の構造体を定義しています.
メンバはnember_name_1~nember_name_nのn個です.
メンバはそれぞれに型typeで定義されています.

①struct tag struct_name;
②struct tag *struct_p;
構造体変数struct_nameの宣言

(1)定義はキーワードstructで始めます.
(2)struct tagが構造体の名前で,他の型と同じように,宣言・定義に使います.
(3)メンバの定義は,通常の変数の宣言と同じように行います.
(4)構造体変数struct_nameを通常の変数の宣言と同じように行います.

 ①のように宣言したときは,プログラムの中では,struct_name.member_name_1やstruct_name.member_nのように演算子「.」を使ってメンバアクセスします.struct_name.member_name_1のように記述すると,メンバを通常の変数のように扱えます.
 ②のようにstruct_pが構造体を指すポインタのときは,プログラムの中では,stru_array->member_name_nのように演算子「->」を使ってメンバにアクセスします.

2.バイナリーファイルのライト

FILE *fp;
void *data;
size_t size;
size_t n;
size_t i;
i = fwrite( data, size, n, fp );

 ファイルポインタfpが指すファイルへ,ポインタdataが指すデータをn個書き込みます.変数sizeはdataの指すデータ1個分の長さ(バイト数)です.通常バイト数は関数sizeof()の戻り値を使って記述します.なお型size_tはunsigned int(符号なし整数)と同じで,ヘッターファイルstdio.hで定義されています.戻り値は書き込んだデータの個数です.正常に書き込みが行われるとi==nになります.

3.バイナリーファイルのリード

FILE *fp;
void *data;
size_t size;
size_t n;
size_t i;
i = fread( data, size, n, fp );

 ファイルポインタfpが指すファイルから,ポインタdataが指すデータをn個読み込みます.変数sizeはdataの指すデータ1個分の長さ(バイト数)です.通常バイト数は関数sizeof()の戻り値を使って記述します.戻り値は読み込んだデータの個数です.正常に読み込みが行われるとi==nになります.




このドキュメントは http://icrus.org/c_language_beginers_course/ 上にあります.

2017,1 ssatoh@ 足立工科大学 工学部 情報通信工学科