【C言語】fread()・fwrite()関数の使い方を解説!構造体データをファイルに保存して読み出す方法
課題
fread()関数とfwrite()関数を使用して、学生の詳細情報をファイルに保存し、その内容を再度読み出して画面に表示するCプログラムを作成します。
解決策
fread()関数は、レコード(構造体)全体を一度に読み込むための関数です。1バイトずつではなく、まとまったデータとして高速に入出力できるのが特徴です。
書式(構文)
fread( &構造体変数, sizeof(構造体変数), 読み込むレコード数, ファイルポインタ );
使用例
struct emp{
int eno;
char ename[30];
float sal;
} e;
FILE *fp;
fread( &e, sizeof(e), 1, fp );fwrite()関数は、レコード(構造体)全体を一度にファイルへ書き込むための関数です。
書式(構文)
fwrite( &構造体変数, sizeof(構造体変数), 書き込むレコード数, ファイルポインタ );
使用例
struct emp{
int eno;
char ename[30];
float sal;
} e;
FILE *fp;
fwrite( &e, sizeof(e), 1, fp );プログラム全体
以下は、学生の学籍番号・氏名・点数を構造体配列に格納し、fwrite()でファイルに書き込み、fread()で読み出して表示する完全なプログラム例です。ここでは処理件数を2人としていますが、ループ回数を変更すれば5人など任意の人数に対応できます。
#include<stdio.h>
struct student{
int sno;
char sname[30];
float marks;
char temp;
};
main( ){
struct student s[60];
int i;
FILE *fp;
fp = fopen( "student1.txt", "w" );
for( i=0; i<2; i++ ){
printf( "学生%dの詳細を入力してください\n", i+1 );
printf( "学籍番号:" );
scanf( "%d", &s[i].sno );
scanf( "%c", &s[i].temp );
printf( "氏名:" );
gets( s[i].sname );
printf( "点数:" );
scanf( "%f", &s[i].marks );
fwrite( &s[i], sizeof(s[i]), 1, fp );
}
fclose( fp );
fp = fopen( "student1.txt", "r" );
for( i=0; i<2; i++ ){
printf( "学生%dの詳細は以下の通りです\n", i+1 );
fread( &s[i], sizeof(s[i]), 1, fp );
printf( "学籍番号 = %d\n", s[i].sno );
printf( "氏名 = %s\n", s[i].sname );
printf( "点数 = %f\n", s[i].marks );
}
fclose( fp );
getch( );
}
実行結果
学生1の詳細を入力してください
学籍番号:1
氏名:pinky
点数:56
学生2の詳細を入力してください
学籍番号:2
氏名:rosy
点数:87
学生1の詳細は以下の通りです
学籍番号 = 1
氏名 = pinky
点数 = 56.000000
学生2の詳細は以下の通りです
学籍番号 = 2
氏名 = rosy
点数 = 87.000000
解説のポイント
- tempメンバーの役割: scanf("%d")で数値を読み込んだ後、入力バッファには改行文字が残ります。この余分な改行を一時的に受け取るためにchar型のtempメンバーが用意されています。
- バイナリモードの推奨: 構造体全体をそのまま読み書きする場合、fopen()のモードは"w"や"r"よりも"wb"や"rb"(バイナリモード)を指定する方が移植性が高く安全です。
- gets()の注意点: gets()はバッファオーバーフローの危険があるため、現在では非推奨の関数です。実際の開発ではfgets()を使用することをおすすめします。
-
C言語入門:ポインタ配列とポインタへのポインタ(二重ポインタ)の基本をわかりやすく解説
はじめにC言語において、ポインタはメモリを直接扱える強力な機能です。本記事では、その応用形である「ポインタ配列」と「ポインタへのポインタ(二重ポインタ)」について、宣言方法・初期化方法・値へのアクセス方法を、サンプルコードと実行結果とともに順を追って解説します。 ポインタ配列とは通常の変数と同様に、ポインタも配列として宣言することができます。これを「ポインタ配列」と呼びます。各要素がそれぞれ別のアドレスを保持できるため、複数のデータを間接的に扱う際に便利です。 宣言方法データ型 *ポインタ名 [サイズ];例えば、次のように宣言します。int *p[5]; // int型の要素アドレスを5つ格納
-
【C言語入門】ポインタと配列の関係をわかりやすく解説|スケールファクタとは
ポインタと配列の基本的な関係 C言語では、コンパイラが配列のすべての要素に対して連続したメモリ領域を自動的に割り当てます。ここで重要になるのが「ベースアドレス」です。ベースアドレスとは、配列の先頭要素(0番目の要素)が格納されているメモリアドレスのことを指します。 例えば、次のように配列を宣言してみましょう。 int a[5] = {10, 20, 30, 40, 50}; この場合、5つの要素はメモリ上に以下のように順番に格納されます。 ここで変数「p」をint型へのポインタとして宣言すれば、次のいずれかの代入文によって、配列aの先頭アドレスをポインタに持たせることができます。 p =