ファイルの読み書き
実用的なプログラムには、ファイルの入出力が不可欠です。この章では、ファイルの入出力の方法について解説します。
ファイルの入出力を行うためには、以下の手順を行う必要があります。
- Step1:ファイルポインタの宣言
- Step2:ファイルのオープン
- Step3:ファイルの読み書き
- Step4:ファイルのクローズ
それでは、それぞれのステップについて説明をしていきます。
ファイルポインタについて
ファイルポインタは、ファイルの入出力を行う際に、読み込みや書き込みを行う位置など、ファイル操作に必要な情報を保持するFILE型の変数です。
ファイルを開いた時に、読み込まれたファイルのメモリ領域のアドレスを保存します。
(FILE型は、typedefを使用して構造体にFILEという名前を付けたものです。)
ファイル操作を行う処理の最初に、以下のように宣言します。
FILE *fp;
ファイルのオープン
ファイル入出力を行う際には、ファイルを開く処理を必ず実施し、ファイルポインタを取得することが必要です。ファイルを開くためにはfopen()関数やfopen_s()関数が使用できますが、ファイル保護を設定して排他的にアクセスするためには、実務ではfopen_s()関数が用いられることが一般的です。
※fopen_s()関数はVisual Studio環境では使用可能ですが、gccのバージョンや環境によっては対応していないことがあります。そのため、ご使用の環境で動作するかどうかをご確認ください。
以下にfopen()とfopen_s()関数の書式を示しますが、これからのサンプルではfopen_s()を使用します。
FILE *fopen(const char *filename, const char *mode);
const char *filename
:オープンするファイル名を指定します。const char *mode
:ファイルをオープンするモードを指定します。
戻り値:ファイルが正常にオープンできた場合は、ファイルポインタが返ります。エラーのときはNULLが返ります。
errno_t fopen_s( FILE** fp, const char *filename, const char *mode);
FILE** fp
: 開かれたファイルへのポインターを受け取るファイル ポインターへのポインター。const char *filename :
オープンするファイル名を指定します。const char *mode
: ファイルをオープンするモードを指定します。
戻り値:正常終了した場合は 0 を返します。失敗した場合はエラー コードを返します。
オープンするモードについて
ファイルには、テキストファイルとバイナリファイルがあります。
テキストファイルとは、メモ帳や他のテキストエディタで文字データとして読むことができるファイルのことです。一方、バイナリファイルは、画像や音声のように文字データとして読むことができないファイルを指します。
ファイルをオープンするときは、テキストファイルかバイナリファイルか、新しいファイルを作成するか、既存のファイルに上書きするかなど、モードを指定する必要があります。
以下にモードの一覧を記載します。
モード | 機能 | ファイルがある場合 | ファイルがない場合 |
---|---|---|---|
r | 読み込み専用で開く | 読み込み開始位置は先頭 | エラー |
w | 書き込み専用で開く | 中身を破棄する。書き込み開始位置は先頭 | 新規に作成する |
a | 追加専用で開く | 書き込み開始位置は末尾 | 新規に作成する。書き込み開始位置は末尾 |
上記の説明で記載した「位置」とは、テキストエディタのカーソルに相当し、この位置からデータの読み込みや書き込みが実行されます。ファイルポインタはこの位置情報を維持しています。
・読み書きの両方に対応する場合「r」,「w」,「a」に「+」を付加します。
ファイルの有無による動作の違いは「r」,「w」,「a」と等しくなります。
記載方法:”r+”,”w+”,”a+”
・バイナリファイルを扱う場合、「r」,「w」,「a」,「r+」,「w+」,「a+」に「b」を付加します。
ファイルの有無による動作の違いは「r」,「w」,「a」と等しくなります。
記載方法:”rb”,”wb”,”ab”
”rb+”,”wb+”,”ab+”または”r+b”,”w+b”,”a+b”
ファイルのクローズ
ファイルをクローズする際には、fclose()関数を用いてください。fopen()、fopen_s()関数でファイルをオープンした後は、必ず、fclose()で閉じることが重要です。
int fclose(FILE *fp);
FILE *fp : fopen()、fopen_s()でオープンしたファイルポインタ
戻り値:成功した場合0、失敗した場合EOFを返します。
fopen_s、fcloseを用いた例を以下に示します。
#include <stdio.h>
int main(void){
FILE *fp;
errno_t err;
err = fopen_s(&fp, "test.txt","r+");
if (err != 0){
printf("ファイルオープンに失敗しました");
return 1;
}
<ファイルの読み書き処理 >
fclose(fp);
return 0;
}
その他にファイルを開く関数には、open、closeなどがあります。
ファイルを読み書きする関数
ファイルからデータを読み込むための関数にはfread、fscanf、fgetsなどがあり、データをファイルに書き込む関数にはfwrite、fprintf、fputsなどが存在します。次に、fgets、fputs、fread、fwriteについて説明します。
1.fgets()
改行を区切りとして、テキストファイルから一行ずつ文字列を読み込む関数です。
現在のファイルポインタの位置から、最初の改行文字 (\n) が見つかるか、ファイルの終わりに達するか、読み取る文字数が「指定された引数の最大値-1」に達するまでの、どちらか早い方まで文字を読み取ります。
#include <stdio.h>
char *fgets (char *string, int count, FILE *fp);
char *string
: 読み取った結果を格納し末尾にNULL(’\0’)を追加します。int count
: 読み取る文字数の最大値を指定します。FILE *fp
:ファイルポインタを指定します。
戻り値:成功した場合は文字配列の要素を指すポインタ、ファイルの最後に到達した場合またはエラーが発生した場合NULLを返します。
fgetsを使用したサンプルプログラムを以下に示します。
このサンプルプログラムは、test.txtファイルを開き、fgets()関数を使用して最大256バイトの文字列をNULLが出現するまで1行ごとに読み込んでいます。
#include <stdio.h>
int main(void){
FILE *fp;
errno_t err;
char s[256];
err = fopen_s(&fp, "test.txt","r");
if (err != 0){
printf("ファイルオープンに失敗しました");
return 1;
}
while( fgets(s,sizeof(s),fp) != NULL){
printf("%s",s);
}
fclose(fp);
return 0;
}
2.fputs()
文字列の内容をテキストファイルに書き出します。現在のファイルポインタの位置から文字列を出力しますが、文字列の最後にあるNULL文字(‘\0’)は含まれません。
#include <stdio.h>
int fputs(const char *string, FILE *fp);
const char *string
: 出力する文字列を指定します。改行させたい場合は、改行コード「\n」付きの文字列を指定します。FILE *fp
:出力先のファイルポインタを指定します。
戻り値:出力が成功した場合は負ではない値を返し、失敗した場合はEOF(-1)が返されます。
このサンプルプログラムは、入力ファイルから一行ずつ読み込んで、出力ファイルに書き出しています。
#include <stdio.h>
int main(void){
FILE *fp;
FILE *outfp;
errno_t err;
char s[256];
err = fopen_s(&fp, "test.txt","r");
if (err != 0){
printf("ファイルオープンに失敗しました");
return 1;
}
err = fopen_s(&outfp, "output.txt","w");
if (err != 0){
printf("ファイルオープンに失敗しました");
return 1;
}
while( fgets(s,sizeof(s),fp) != NULL){
if(fputs(s,outfp) == EOF) break;
}
fclose(fp);
return 0;
}
3.fwrite()
指定された要素数を最大値として、指定されたサイズのデータをファイルに書き込みます。
配列などの大量のデータを書き込むときなどに使用されます。
ファイルはバイナリ形式、テキストファイル形式どちらでも使用することができます。
#include <stdio.h>
size_t fwrite(const void *buffer, size_t size, size_t count, FILE *fp);
const void *buffer
:書き込みデータのポインタを指定します。size_t size
:書き込みデータのバイト長さを指定します。size_t count
:書き込みデータの要素数を指定します。FILE *fp
:ファイルポインタを指定します。
戻り値:書き込み成功した場合データの要素数を、エラーが発生した場合countより小さい値を返します。
以下は、構造体の配列のデータをファイルへ書き込むサンプルプログラムです。
#include <stdio.h>
typedef struct{
double spx;
double spy;
double epx;
double epy;
int thick;
} LINE;
int main(void){
LINE line[] = {
{0.0,0.0,20.0,0.0,1},
{20.0,0.0,20.0,10.0,2},
{0.0,10.0,20.0,10.0,3},
{0.0,0.0,0.0,10.0,4}
};
FILE *fp;
int err;
for(int i=0;i<4;i++){
printf("%d:(%.1f,%.1f)-(%.1f,%.1f) thick=%d\n",i,line[i].spx,line[i].spy,line[i].epx,line[i].epy,line[i].thick);
}
err = fopen_s(&fp,"zahyou.bin","wb");
fwrite(line, sizeof(LINE), sizeof(line) / sizeof(line[0]),fp);
fclose(fp);
return 0;
}
sizeof(LINE)
は書き込むデータのバイト長を指定しています。sizeof(line) / sizeof(line[0])
は配列の要素数を計算しています。
構造体配列のデータをそのまま「zahyou.bin」というデータファイルに書き込んでいます。
実行結果
0:(0.0,0.0)-(20.0,0.0) thick=1
1:(20.0,0.0)-(20.0,10.0) thick=2
2:(0.0,10.0)-(20.0,10.0) thick=3
3:(0.0,0.0)-(0.0,10.0) thick=4
4.fread()
指定されたサイズのデータをファイルから読み込み、その数だけ配列に格納します。
ファイルはバイナリ形式、テキストファイル形式どちらでも使用することができます。
#include <stdio.h>
size_t fread(void *buffer, size_t size, size_t count, FILE *fp);
void *buffer
:読み込みデータ格納先のポインタを指定します。size_t size
:読み込みデータのバイト長を指定します。size_t count
:読み込みデータの要素数を指定します。FILE *fp
:ファイルポインタを指定します。
戻り値:読み込みに成功した場合データの要素数を、ファイルが終了した場合、あるいはエラーが発生した場合countより小さい値を返します。
以下は、fwriteで書き込んだファイルの内容を構造体配列の変数に読み込んで表示するサンプルプログラムです。
#include <stdio.h>
typedef struct{
double spx;
double spy;
double epx;
double epy;
int thick;
} LINE;
int main(void){
FILE *fp;
int err;
LINE line[4] = {0};
err = fopen_s(&fp,"zahyou.bin","rb");
fread(line,sizeof(LINE),sizeof(line)/sizeof(line[0]),fp);
for(int i=0;i<4;i++){
printf("%d:(%.1f,%.1f)-(%.1f,%.1f) thick=%d\n",i,line[i].spx,line[i].spy,line[i].epx,line[i].epy,line[i].thick);
}
fclose(fp);
return 0;
}
実行結果
0:(0.0,0.0)-(20.0,0.0) thick=1
1:(20.0,0.0)-(20.0,10.0) thick=2
2:(0.0,10.0)-(20.0,10.0) thick=3
3:(0.0,0.0)-(0.0,10.0) thick=4
コメント