C言語 ファイル入出力: fopen, fprintf, fread, fseek をマスターする
C言語 ファイル入出力
このチュートリアルでは、C言語のファイルI/Oを、標準およびバイナリ操作の両面から明確で実践的な例とともに解説します。
ファイルは、コンピュータのストレージ上の論理的なコンテナで、データを長期利用のために保持します。
なぜファイルを使うのか?
- プログラム終了後もデータが持続し、情報の損失を防げます。
- 大量データセットを一度保存すれば、わずかなCコマンドで高速アクセス可能です。
- ファイルは変更なしでコンピュータ間を移動できます。
ファイルの種類
C言語では主に2種類のファイルを扱います:
- テキストファイル
- バイナリファイル
テキストファイル
プレーンテキストファイル(例:.txt)は任意のテキストエディタで編集可能。読みやすく、保守・転送が容易ですが、サイズが大きくなり、セキュリティは最小限です。
バイナリファイル
バイナリファイル(一般に.bin)はデータを生のバイト(0と1)として格納。コンパクトで人間非可読、データ整合性とセキュリティに優れます。
コアファイル操作
ファイル種別に関わらず、C言語のファイル操作は4つの基本操作を中心に回ります:
- 新規ファイルの作成
- 既存ファイルのオープン
- ファイルのクローズ
- ファイルからの読み取り/書き込み
ファイルポインタの準備
すべてのファイル操作には、開いたファイルを参照するFILE *ポインタが必要です:
FILE *fptr;
ファイルのオープン: 作成と編集
fopen()関数(stdio.hで宣言)はファイルを開き、FILE *ポインタを返します。構文:
fptr = fopen("filename", "mode");
使用例:
fopen("E:\\cprogram\\newprogram.txt", "w");
fopen("E:\\cprogram\\oldprogram.bin", "rb");
解説:
newprogram.txtは(存在しなければ)作成され、モード'w'で書き込みオープン。既存内容は上書きされます。oldprogram.binはモード'rb'でバイナリ読み取り専用オープン。書き込みは失敗します。
| モード | 意味 | ファイル不在時の挙動 |
|---|---|---|
r | 読み取り用オープン | NULLを返す |
rb | バイナリ読み取り用オープン | NULLを返す |
w | 書き込み用オープン(上書き) | 不在なら作成 |
wb | バイナリ書き込み用オープン(上書き) | 不在なら作成 |
a | 追記用オープン | 不在なら作成 |
ab | バイナリ追記用オープン | 不在なら作成 |
r+ | 読み書き両用 | NULLを返す |
rb+ | バイナリ読み書き両用 | NULLを返す |
w+ | 読み書き両用(上書き) | 不在なら作成 |
wb+ | バイナリ読み書き両用(上書き) | 不在なら作成 |
a+ | 読み取り・追記両用 | 不在なら作成 |
ab+ | バイナリ読み取り・追記両用 | 不在なら作成 |
ファイルのクローズ
すべてのI/O操作後、必ずfclose()でファイルを閉じ、リソースを解放しバッファをフラッシュします:
fclose(fptr);
テキストファイル I/O
テキストファイルはfprintf()とfscanf()で扱います。これらはprintf()/scanf()のファイル版で、第1引数にFILE *ポインタを取ります。
例1: テキストファイルへの書き込み
#include <stdio.h>
#include <stdlib.h>
int main() {
int num;
FILE *fptr;
// OSに合わせてパス調整
fptr = fopen("C:\\program.txt", "w");
if (fptr == NULL) {
printf("Error opening file\n");
exit(EXIT_FAILURE);
}
printf("数値を入力: ");
scanf("%d", &num);
fprintf(fptr, "%d", num);
fclose(fptr);
return 0;
}
このプログラムはユーザーから整数を受け取り、program.txtに書き込みます。
例2: テキストファイルからの読み取り
#include <stdio.h>
#include <stdlib.h>
int main() {
int num;
FILE *fptr;
fptr = fopen("C:\\program.txt", "r");
if (fptr == NULL) {
printf("Error opening file\n");
exit(EXIT_FAILURE);
}
fscanf(fptr, "%d", &num);
printf("Value read: %d\n", num);
fclose(fptr);
return 0;
}
例1の後に実行すると、格納された整数が表示されます。
fgetc()やfputc()などの追加ユーティリティも同じパターンで使えます。
バイナリファイル I/O
バイナリファイルは、書き込みにfwrite()、読み取りにfread()で操作します。両関数は4引数:データへのポインタ、要素サイズ、要素数、FILE *ポインタを取ります。
バイナリファイルへの書き込み
fwrite(addressData, sizeData, numberData, filePointer);
例3: fwrite()でバイナリファイルに書き込み
#include <stdio.h>
#include <stdlib.h>
struct threeNum {
int n1, n2, n3;
};
int main() {
int i;
struct threeNum num;
FILE *fptr;
fptr = fopen("C:\\program.bin", "wb");
if (fptr == NULL) {
printf("Error opening file\n");
exit(EXIT_FAILURE);
}
for (i = 1; i < 5; ++i) {
num.n1 = i;
num.n2 = 5 * i;
num.n3 = 5 * i + 1;
fwrite(&num, sizeof(struct threeNum), 1, fptr);
}
fclose(fptr);
return 0;
}
バイナリファイルからの読み取り
対応するfread()のシグネチャはfwrite()と対称です:
fread(addressData, sizeData, numberData, filePointer);
例4: fread()でバイナリファイルから読み取り
#include <stdio.h>
#include <stdlib.h>
struct threeNum {
int n1, n2, n3;
};
int main() {
int i;
struct threeNum num;
FILE *fptr;
fptr = fopen("C:\\program.bin", "rb");
if (fptr == NULL) {
printf("Error opening file\n");
exit(EXIT_FAILURE);
}
for (i = 1; i < 5; ++i) {
fread(&num, sizeof(struct threeNum), 1, fptr);
printf("n1: %d\tn2: %d\tn3: %d\n", num.n1, num.n2, num.n3);
}
fclose(fptr);
return 0;
}
このプログラムは例3で書き込んだデータを再現します。
fseek()によるランダムアクセス
ファイルに多くのレコードがある場合、特定のエントリまで順次走査するのは非効率です。fseek()はファイルポインタを任意の位置に移動させ、直接アクセスを可能にします。
構文
fseek(FILE *stream, long int offset, int whence);
stream:fopen()が返したポインタ。offset: 移動バイト数。whence: 起点 –SEEK_SET、SEEK_CUR、SEEK_END。
| 定数 | 意味 |
|---|---|
SEEK_SET | ファイル先頭からのオフセット。 |
SEEK_CUR | 現在のカーソル位置からのオフセット。 |
SEEK_END | ファイル末尾からのオフセット。 |
例5: fseek()で逆順読み取り
#include <stdio.h>
#include <stdlib.h>
struct threeNum {
int n1, n2, n3;
};
int main() {
int i;
struct threeNum num;
FILE *fptr;
fptr = fopen("C:\\program.bin", "rb");
if (fptr == NULL) {
printf("Error opening file\n");
exit(EXIT_FAILURE);
}
// 最後のレコードに移動
fseek(fptr, -sizeof(struct threeNum), SEEK_END);
for (i = 1; i < 5; ++i) {
fread(&num, sizeof(struct threeNum), 1, fptr);
printf("n1: %d\tn2: %d\tn3: %d\n", num.n1, num.n2, num.n3);
// 2レコード分戻る(現在位置+1つ前)て前のエントリを読む
fseek(fptr, -2 * sizeof(struct threeNum), SEEK_CUR);
}
fclose(fptr);
return 0;
}
このスニペットは、バイナリファイルを最後のレコードから逆順に走査する方法を示しています。
C言語
- STLファイル理解: 3Dプリントの基盤
- C言語ファイルI/O完全ガイド:作成、オープン、管理の実践
- C言語ヘッダーファイルの理解:種類、使い方、ベストプラクティス
- C言語エラーハンドリングのマスター:ベストプラクティスとテクニック
- C++ファイルI/O完全マスター:fstreamによる読み書き
- C++シグナル処理をマスター:安全な割り込み管理とクリーンなプログラム終了
- C#の例外処理:try/catch/finally/throwとベストプラクティス
- C#ファイルI/O:Stream、FileStream、StreamReader/Writerによる読み書き
- VerilogファイルI/Oの完全ガイド:オープン、読み取り、書き込み、クローズ
- PythonファイルI/O:入出力操作をマスターする
- Javaファイル操作のマスター:java.ioによる作成・読み取り・書き込み・削除