fopen() / fclose() / freopen()
| 対応: | C89(1989) |
|---|
ファイルを開いたり閉じたりするための基本的な関数です。『<stdio.h>』に定義されており、ファイル操作を行うすべての処理の起点になります。ファイルはオープンしたら必ずクローズする必要があります。
構文
// ファイルをオープンして FILE ポインタを返します。 // 戻り値: 成功時は FILE ポインタ、失敗時は NULL。 FILE *fopen(const char *filename, const char *mode); // ファイルをクローズします。 // 戻り値: 成功時は 0、エラー時は EOF。 int fclose(FILE *stream); // 既存の FILE ポインタを別のファイルに結び付けます(リダイレクトに便利)。 // 戻り値: 成功時は stream、失敗時は NULL。 FILE *freopen(const char *filename, const char *mode, FILE *stream);
モード文字列一覧
| モード | 概要 |
|---|---|
| "r" | 読み込み専用でオープンします。ファイルが存在しない場合は NULL を返します。 |
| "w" | 書き込み専用でオープンします。ファイルが存在しない場合は新規作成、存在する場合は内容を消去します。 |
| "a" | 追記モードでオープンします。ファイルが存在しない場合は新規作成、存在する場合は末尾から書き込みます。 |
| "r+" | 読み書き両用でオープンします。ファイルが存在しない場合は NULL を返します。 |
| "w+" | 読み書き両用でオープンします。存在する場合は内容を消去します。 |
| "a+" | 読み書き両用の追記モードでオープンします。書き込み位置は常に末尾です。 |
| "rb", "wb" 等 | バイナリモードでオープンします。テキストモードとの違いは改行文字の扱いです(Windows 環境で重要)。 |
サンプルコード
sample_fopen_fclose.c
#include <stdio.h>
int main(void) {
// "w" モードでファイルを作成し、テキストを書き込みます。
FILE *fp = fopen("sample.txt", "w");
if (fp == NULL) {
// fopen が失敗した場合(パスが存在しない・権限がないなど)。
perror("fopen");
return 1;
}
fprintf(fp, "1行目のテキストです\n");
fprintf(fp, "2行目のテキストです\n");
fprintf(fp, "3行目のテキストです\n");
fclose(fp); // 書き込み後は必ずクローズします。
// "r" モードで先ほど作成したファイルを読み込みます。
fp = fopen("sample.txt", "r");
if (fp == NULL) {
perror("fopen");
return 1;
}
char buf[256];
while (fgets(buf, sizeof(buf), fp) != NULL) {
printf("%s", buf); // 1行ずつ標準出力に表示します。
}
fclose(fp);
// "a" モードで追記します(既存の内容は保持されます)。
fp = fopen("sample.txt", "a");
if (fp == NULL) {
perror("fopen");
return 1;
}
fprintf(fp, "4行目(追記)です\n");
fclose(fp);
printf("追記が完了しました。\n");
return 0;
}
コンパイルして実行すると次のようになります。
gcc sample_fopen_fclose.c -o sample_fopen_fclose ./sample_fopen_fclose 1行目のテキストです 2行目のテキストです 3行目のテキストです 追記が完了しました。
freopen でストリームを切り替える
freopen は既存の FILE ポインタを別のファイルに結び付けます。標準出力をファイルにリダイレクトする場合などに使います。
freopen_example.c
#include <stdio.h>
int main(void) {
printf("これは標準出力に出力されます。\n");
if (freopen("stdout_log.txt", "w", stdout) == NULL) {
perror("freopen");
return 1;
}
printf("これはファイルに書き込まれます。\n");
printf("標準出力がリダイレクトされています。\n");
fclose(stdout);
FILE *fp = fopen("stdout_log.txt", "r");
if (fp == NULL) { perror("fopen"); return 1; }
char buf[128];
while (fgets(buf, sizeof(buf), fp) != NULL) {
fprintf(stderr, "ファイル内容: %s", buf);
}
fclose(fp);
return 0;
}
コンパイルして実行すると次のようになります。
gcc freopen_example.c -o freopen_example ./freopen_example これは標準出力に出力されます。 ファイル内容: これはファイルに書き込まれます。 ファイル内容: 標準出力がリダイレクトされています。
バイナリモードでのファイル操作
バイナリデータを扱う場合は "rb" / "wb" でファイルを開きます。テキストモードでは改行変換が行われるため、バイナリデータが壊れることがあります。
fopen_binary.c
#include <stdio.h>
int main(void) {
unsigned char data[] = {0x00, 0x01, 0xFF, 0x7F, 0x80};
int n = (int)(sizeof(data) / sizeof(data[0]));
FILE *fp = fopen("binary.bin", "wb");
if (fp == NULL) { perror("fopen"); return 1; }
fwrite(data, sizeof(unsigned char), n, fp);
fclose(fp);
unsigned char buf[8] = {0};
fp = fopen("binary.bin", "rb");
if (fp == NULL) { perror("fopen"); return 1; }
size_t read_n = fread(buf, sizeof(unsigned char), n, fp);
fclose(fp);
printf("読み込んだバイト数: %zu\n", read_n);
for (size_t i = 0; i < read_n; i++) {
printf("0x%02X ", buf[i]);
}
printf("\n");
return 0;
}
コンパイルして実行すると次のようになります。
gcc fopen_binary.c -o fopen_binary ./fopen_binary 読み込んだバイト数: 5 0x00 0x01 0xFF 0x7F 0x80
よくあるミス
よくあるミス: NULL チェックの省略
fopen が返す NULL をチェックしないと、ファイルが存在しない場合にプログラムがクラッシュします。
fopen_null_ng.c
#include <stdio.h>
int main(void) {
FILE *fp = fopen("nonexistent.txt", "r");
/* NG: NULL チェックなし */
char buf[64];
fgets(buf, sizeof(buf), fp); /* fp が NULL なのでクラッシュする */
printf("%s", buf);
fclose(fp);
return 0;
}
修正後は次の通りです。
gcc fopen_null_ng.c -o fopen_null_ng ./fopen_null_ng Segmentation fault (core dumped)
fopen_null_ok.c
#include <stdio.h>
int main(void) {
FILE *fp = fopen("nonexistent.txt", "r");
if (fp == NULL) {
perror("fopen");
return 1;
}
char buf[64];
while (fgets(buf, sizeof(buf), fp) != NULL) {
printf("%s", buf);
}
fclose(fp);
return 0;
}
修正後は次の通りです。
gcc fopen_null_ok.c -o fopen_null_ok ./fopen_null_ok fopen: No such file or directory
概要
『fopen()』が返す FILE ポインタは、ファイルの読み書き位置や状態を管理する構造体へのポインタです。このポインタを『fgets()』・『fread()』・『fseek()』など各ファイル操作関数に渡して使用します。
fopen() の戻り値は必ず NULL チェックをしてください。ファイルが存在しない場合や権限がない場合に NULL が返ります。チェックを怠るとヌルポインタ参照でプログラムがクラッシュします。
オープンしたファイルは必ず fclose() でクローズしてください。クローズしないと書き込みバッファがフラッシュされず、データが正しく保存されないことがあります。また、オープンできるファイル数にはシステムの上限があり、クローズしないとリソースリークが発生します。
ファイルを1行ずつ読み込む場合は『fgets()』、バイナリデータの読み書きには『fread() / fwrite()』を使用してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。