ファイルに整った形でデータを書き出すには、標準出力向けのprintfだけでは不十分です。
fprintfは、同じ書式指定のまま、任意のファイルへ文字列を安全かつ柔軟に出力できる関数です。
本記事ではC言語初心者の方に向けて、基礎から実用的な書式指定、エラー処理まで丁寧に解説します。
fprintfの基本
fprintfとは
fprintfは、指定したファイルストリームに書式付きで文字列を出力する標準関数です。
書式指定はprintfと同じルールで扱えます。
関数プロトタイプ
int fprintf(FILE *stream, const char *format, ...);
- 第1引数には
FILE*
型のストリーム(例:fopen
で開いたファイル)を渡します。 - 第2引数は書式文字列、その後に書式に対応する値を並べます。
- 戻り値は書き込んだ文字数、エラー時は負の値です。
printfとの違い
printfは標準出力(stdout)に書き出すのに対し、fprintfは任意のFILE
に書き出します。
つまり、出力先を自由に切り替えられます。
ログファイル、CSV、レポートなど、ファイル保存を伴う処理に向いています。
なおstderr
などの標準エラー出力もFILE
であり、fprintf(stderr, ...)
のように出力できます(標準入出力の詳細は別記事で扱います)。
使う場面
プログラムの実行結果を後から分析したい時、数値を決めた桁数でそろえたい時、CSVやTSVのような整形テキストを出力したい時に有用です。
フォーマット(書式)をコードで明示できるため、再現性の高い出力が得られます。
使い方の手順
ファイルを開く
まずfopen
でファイルを開きます。
典型的には書き込み用に"w"
または"a"
を使います。
代表的なモードと意味
モード | 意味 |
---|---|
“w” | 新規作成または既存ファイルを上書きして書き込み開始 |
“a” | 既存ファイルの末尾に追記、無ければ新規作成 |
“w+” | 読み書き両用。新規作成または上書き |
“a+” | 読み書き両用。末尾に追記開始 |
“wb”/”ab” | バイナリモードでの書き込み(改行の自動変換を抑制) |
テキストファイルで通常は"w"
か"a"
を使います。
Windowsで改行コードの自動変換を避けたい場合は"wb"
を使います。
書き込む
開いたFILE*
に対してfprintf
を呼ぶだけです。
書式文字列の中に%d
や%f
などの書式指定子を記述し、後ろに値を続けます。
改行やタブの指定
テキストの整形にはエスケープシーケンスを使います。
- 改行:
\n
- タブ:
\t
テキストファイルに対しては一般に\n
を書けば十分です。
OSにより実ファイル上の改行コード表現は異なりますが、テキストモードではランタイムが適切に扱います。
ファイルを閉じる
書き込みが終わったらfclose
でファイルを閉じます。
バッファに溜まっているデータが確実にディスクへ書き出されます。
途中で強制的に書き出したいときはfflush(fp)
を用います。
以下は、タブ区切りの簡単なレポートを作成する例です。
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(void) {
// ファイルを上書きモードで開く
FILE *fp = fopen("report.txt", "w");
if (fp == NULL) {
// 開けない場合のエラー表示
perror("fopen failed");
return 1;
}
// 見出し行を書き込む(タブ区切り)
// \t はタブ、\n は改行です
if (fprintf(fp, "ID\tName\tScore\n") < 0) {
perror("fprintf failed");
fclose(fp);
return 1;
}
// 3件のレコードを書き込む
int id1 = 101, id2 = 102, id3 = 103;
const char *name1 = "Alice";
const char *name2 = "Bob";
const char *name3 = "Carol";
double score1 = 87.5, score2 = 92.0, score3 = 78.25;
// %.2f は小数点以下2桁に丸めて表示します
if (fprintf(fp, "%d\t%s\t%.2f\n", id1, name1, score1) < 0) {
perror("fprintf failed");
fclose(fp);
return 1;
}
if (fprintf(fp, "%d\t%s\t%.2f\n", id2, name2, score2) < 0) {
perror("fprintf failed");
fclose(fp);
return 1;
}
if (fprintf(fp, "%d\t%s\t%.2f\n", id3, name3, score3) < 0) {
perror("fprintf failed");
fclose(fp);
return 1;
}
// 必要に応じて明示的にフラッシュ(通常は fclose で十分)
if (fflush(fp) == EOF) {
perror("fflush failed");
fclose(fp);
return 1;
}
// ファイルを閉じる
if (fclose(fp) == EOF) {
perror("fclose failed");
return 1;
}
return 0;
}
ID Name Score
101 Alice 87.50
102 Bob 92.00
103 Carol 78.25
fprintfは戻り値で成功・失敗を判定できます。
上の例では、書き込みのたびに0未満かをチェックしています。
書式指定子の基礎
%d %f %s %cの使い方
基本的な書式指定子は次のとおりです。
型と書式は一致させることが重要です。
%d
: 符号付き整数(int)%f
: 実数(double)。floatは可変長引数でdoubleに昇格します%s
: 文字列(charポインタ)%c
: 1文字(char)
桁幅と小数点の指定
数値や文字列をそろえたい場合は、桁幅(width)と精度(precision)を使います。
%8d
: 右寄せで最低8桁分を確保%-8d
: 左寄せで最低8桁%08d
: 空き部分を0で埋めて8桁%.2f
: 小数点以下2桁%8.3f
: 全体幅8、少数3桁、足りない部分は空白
文字列内の%を出す
%%
と書くと、実際に%記号を1文字出力します。
fprintf(fp, "Progress: 50%%\n");
複数の値をまとめて出力
1回のfprintf呼び出しで複数の値を整形しながら一度に出力できます。
フォーマットと値の順序を合わせましょう。
以下は各書式指定子と桁幅・精度をまとめて確認する例です。
#include <stdio.h>
int main(void) {
FILE *fp = fopen("format_demo.txt", "w");
if (!fp) {
perror("fopen failed");
return 1;
}
int n = 123;
double pi = 3.1415926535;
const char *word = "hello";
char ch = 'X';
// 基本の出力
fprintf(fp, "int: %d\n", n);
fprintf(fp, "double: %f\n", pi); // デフォルトは6桁表示
fprintf(fp, "double(%.2f): %.2f\n", pi, pi); // 小数点以下2桁
fprintf(fp, "string: %s\n", word);
fprintf(fp, "char: %c\n", ch);
// 桁幅・揃え・ゼロ埋め
fprintf(fp, "right width: [%8d]\n", n);
fprintf(fp, "left width: [%-8d]\n", n);
fprintf(fp, "zero pad : [%08d]\n", n);
// 浮動小数の幅と精度
fprintf(fp, "float width.prec : [%8.3f]\n", pi);
// 文字列内の % を出す
fprintf(fp, "Progress: 50%% done.\n");
// 複数値の同時出力
fprintf(fp, "mix: id=%04d, name=%-8s, score=%.1f\n", 7, "Kai", 81.0);
fclose(fp);
return 0;
}
int: 123
double: 3.141593
double(%.2f): 3.14
string: hello
char: X
right width: [ 123]
left width: [123 ]
zero pad : [00000123]
float width.prec : [ 3.142]
Progress: 50% done.
mix: id=0007, name=Kai , score=81.0
書式指定は「%[フラグ][幅][.精度][長さ]変換子」の順です。
初心者のうちは、-
(左寄せ)、0
(ゼロ埋め)、幅
、.精度
から慣れていくと良いでしょう。
エラー処理と注意点
ファイルを開けない時の対処
fopenがNULLを返したら開けていません。
原因はパスの間違い、権限不足、ディレクトリ未作成などが考えられます。
perror
やstrerror(errno)
で詳細を表示し、必要ならディレクトリ作成や権限の見直しを行います。
#include <stdio.h>
#include <errno.h>
#include <string.h>
FILE *open_report(const char *path) {
FILE *fp = fopen(path, "w");
if (!fp) {
fprintf(stderr, "cannot open %s: %s\n", path, strerror(errno));
return NULL;
}
return fp;
}
戻り値の確認
fprintfの戻り値は書き込んだ文字数、エラー時は負数です。
失敗時はferror(fp)
が非0になっているかも確認できます。
int wrote = fprintf(fp, "value=%d\n", 42);
if (wrote < 0) {
perror("fprintf failed");
// 必要ならリカバリ処理
}
追記と上書きの違い
上書き(“w”)は既存内容を破棄、追記(“a”)は末尾に追加します。
ログや履歴は追記、結果ファイルの再生成は上書きを選びます。
目的 | 推奨モード | 例 |
---|---|---|
ログを増やす | “a” | 実行ごとに末尾へ追加 |
結果を作り直す | “w” | 既存内容をすべて置き換える |
読み書きテスト | “w+” / “a+” | 書いた後に読み返すなど |
バッファとfflushの基本
ファイルストリームは通常全バッファリングです。
fcloseで必ずフラッシュされます。
fflush(fp)
は途中経過をすぐディスクに反映したい時に使います。
毎回呼ぶと性能低下につながるため、必要なタイミングに限定しましょう。
なお標準出力は端末接続時に行バッファリングされますが、通常のファイルは改行で自動フラッシュされません。
型と書式を合わせる
型と書式指定子が一致しないと未定義動作です。
以下を目安にしてください。
int
→%d
、unsigned int
→%u
long
→%ld
、long long
→%lld
size_t
→%zu
double
→%f
、指数表記は%e
、汎用は%g
char *
→%s
、char
→%c
可変長引数ではfloatはdoubleに昇格するため、%.2f
にfloat
を渡しても問題ありません。
一方、例:longに%dなどは危険です。
常に「変数の型」と「%の後の指定」を対応づけましょう。
まとめ
fprintfは「好きなファイルへprintfと同じ感覚で書式付き出力」できる強力な関数です。
使い方の基本は、ファイルをfopen
で開き、fprintf
で書き、必要ならfflush
を行い、最後にfclose
で閉じるだけです。
%dや%fなどの書式指定子、幅や精度の指定、%%で%を出す方法を押さえれば、見やすいレポートやログ、CSVなどを簡単に生成できます。
さらに、戻り値やferrorでのエラー確認、モード選択(上書き/追記)、型と書式の一致を徹底することで、堅牢なファイル出力が実現します。
まずはサンプルを動かして、必要なフォーマットへ少しずつアレンジしてみてください。