閉じる

【C言語】 fprintfの使い方超入門|ファイル出力と書式指定を完全解説

C言語でファイルに文字や数値を書き出すとき、最初に出会うのがfprintfです。

標準出力に表示するprintfとはよく似ていますが、実際には役割も使い方も少し違います。

本記事では、fprintfの基本構文から、ファイル出力の実用サンプル、書式指定のコツ、安全に使うためのポイントまで、初学者の方にもわかりやすく丁寧に解説していきます。

fprintfとは|printfとの違いと基本構文

fprintfの基本構文と引数の意味

fprintfは「指定したファイルストリームに、書式付きでデータを書き込む」関数です。

まずは基本となる関数プロトタイプを確認します。

C言語
int fprintf(FILE *stream, const char *format, ...);

それぞれの引数の意味は次の通りです。

  • FILE *stream
    書き込み先を表すファイルストリームです。
    fopenで開いたファイルや、標準出力stdoutなどを指定します。
  • const char *format
    出力の形を決める書式文字列です。
    中に%d%f%sのようなフォーマット指定子を含め、その順番に応じて後続の引数が埋め込まれます。
  • ...
    可変長引数です。書式文字列に含まれるフォーマット指定子の個数と順番に合わせて、出力したい値を並べて渡します。

戻り値は書き込んだ文字数(バイト数)で、エラーが発生した場合は-1が返されます。

この戻り値を利用することで、後述するようにエラー処理を行うことができます。

printfとの違い

fprintfとprintfの違いは、「出力先を自分で指定できるかどうか」にあります。

printfは、常に標準出力(stdout)へ出力します。

通常はコンソール画面に表示されます。

一方fprintfは、第1引数にFILE *を取るため、

  • 画面(標準出力): fprintf(stdout, ...)
  • 標準エラー出力: fprintf(stderr, ...)
  • 任意のファイル: fprintf(fp, ...)

のように出力先を自由に切り替えられる点が最大の違いです。

実はprintfはfprintfの特殊ケースと考えることができ、実装的には「fprintf(stdout, ...)とほぼ同じもの」として扱われます。

fprintfが必要になる代表的なシーン

fprintfを使う場面は、コンソール表示ではなくファイルやログに記録したいときです。

具体的には次のようなシーンが代表的です。

  • 処理結果や統計情報などをCSVファイルとして出力したい
  • アプリケーションのログファイルを残したい
  • エラー情報を標準エラー出力(stderr)へ出したい
  • デバッグ用に「いつ・どの処理が行われたか」をファイルに書き残したい

このようなとき、printfだけでは出力先を切り替えられないため、fprintfが必要になります。

fprintfによるファイル出力の基本

fopenと組み合わせたfprintfの使い方

ファイルにデータを書き込むには、必ず次の手順を踏みます。

  1. fopenでファイルを開く
  2. 返されたFILE *fprintfに渡して書き込む
  3. fcloseでファイルを閉じる

この3ステップを守ることで、ファイルの壊れや予期せぬデータ消失を防ぐことができます。

ファイルモード(r・w・a)とfprintfの挙動

fopenにはファイルモードを指定します。

よく使うモードと、そのときのfprintfの挙動をまとめると次のようになります。

モード読み書きファイルが存在しない場合既存ファイルがある場合fprintfの書き込み位置
"r"読み込み専用開けない(エラー)既存ファイルを開く原則書き込み不可(未定義動作)
"w"書き込み専用新規作成中身を全消去して開く先頭から書き込み
"a"追記専用新規作成既存ファイルを開く常に末尾に追記

最も注意すべきなのは"w"モードです。

既存ファイルを指定すると内容がすべて消去されてから書き込みが始まるため、大事なファイルを上書きしないよう細心の注意が必要です。

ログを蓄積したい場合や、前の内容を残したまま末尾に追加したい場合は、"a"モードを使うと安全です。

fprintfで文字列・数値をファイルに書き込む例

ここでは、文字列と数値を混ぜてテキストファイルに書き込む簡単な例を示します。

C言語
#include <stdio.h>

int main(void) {
    FILE *fp;
    int id = 1;
    double score = 95.5;
    const char *name = "Yamada";

    /* "w"モードで開くので、既にあれば内容を消去して新規に書き込みます */
    fp = fopen("result.txt", "w");
    if (fp == NULL) {
        /* ファイルが開けなかったときのエラーメッセージを標準エラー出力へ */
        fprintf(stderr, "ファイルを開けませんでした。\n");
        return 1;
    }

    /* 1行目: 文字列だけを書き込む */
    fprintf(fp, "試験結果レポート\n");

    /* 2行目: 数値と文字列を混ぜて書き込む */
    fprintf(fp, "ID=%d, 名前=%s, 得点=%.1f\n", id, name, score);

    /* ファイルを閉じて、バッファの内容を実際のファイルへ確実に書き出します */
    fclose(fp);

    return 0;
}

このプログラムを実行すると、result.txtには次のような内容が書き込まれます。

実行結果
試験結果レポート
ID=1, 名前=Yamada, 得点=95.5

このようにfprintfではprintfと同じ感覚で書式を指定できるため、コンソール表示のコードを一部書き換えるだけで、簡単にファイル出力へ切り替えられます。

fopen・fprintf・fcloseの基本コード例

ここでは、もっとも基本的な「1行だけ書いて終了する」コードを整理して示します。

C言語
#include <stdio.h>

int main(void) {
    /* 1. ファイルポインタの宣言 */
    FILE *fp;

    /* 2. ファイルをテキスト書き込みモード("w")で開く */
    fp = fopen("hello.txt", "w");
    if (fp == NULL) {
        /* 3. エラー処理: 開けなかった場合はメッセージを出して終了 */
        fprintf(stderr, "hello.txt を開けませんでした。\n");
        return 1;
    }

    /* 4. fprintfでファイルへ文字列を書き込む */
    fprintf(fp, "Hello, file output with fprintf!\n");

    /* 5. ファイルを閉じる */
    fclose(fp);

    /* 正常終了 */
    return 0;
}

この手順を基本形のテンプレートとして覚えておくと、さまざまな場面で再利用しやすくなります。

fprintfの書式指定(フォーマット指定子)完全解説

%d・%f・%sなど基本的な書式指定子

fprintfの書式指定は、基本的にprintfとまったく同じです。

よく使うものを簡単に整理します。

指定子対応する型出力例
%dint型(符号付き10進整数)%d42, -10
%uunsigned int型(符号なし)%u0, 100
%fdouble型(浮動小数)%f3.141593 など
%.2fdouble型%.2f3.14, 2.00
%s文字列(char配列/ポインタ)%sHello
%c1文字(char)%cA, 0, !
%%文字%そのもの%%%

実際の使用例を示します。

C言語
#include <stdio.h>

int main(void) {
    FILE *fp = fopen("format_sample.txt", "w");
    if (fp == NULL) {
        fprintf(stderr, "format_sample.txt を開けませんでした。\n");
        return 1;
    }

    int a = 10;
    unsigned int ua = 300u;
    double pi = 3.14159265;
    const char *msg = "format test";

    /* さまざまな型を混ぜて書き込む例 */
    fprintf(fp, "a = %d\n", a);
    fprintf(fp, "ua = %u\n", ua);
    fprintf(fp, "pi = %f\n", pi);
    fprintf(fp, "pi(小数2桁) = %.2f\n", pi);
    fprintf(fp, "msg = %s\n", msg);
    fprintf(fp, "パーセント記号: %%\n");

    fclose(fp);
    return 0;
}

このように、変数の型とフォーマット指定子を必ず対応させることが重要です。

型が合っていないと、未定義動作になり不正な値が出力されたり、最悪の場合はクラッシュの原因になります。

桁数・小数点桁数・右寄せ・左寄せの指定方法

fprintf(printf)のフォーマット指定では、桁数や配置を細かく指定できます。

形としては次のようなパターンを覚えておくと便利です。

  • 桁数(幅)指定: %5d (5桁幅、右寄せ)
  • 小数点以下の桁数: %.2f (小数2桁)
  • 幅と小数の両方: %8.3f (全体8桁、小数3桁)
  • 左寄せ: %-10s (幅10で左寄せ)

これらを使った例を示します。

C言語
#include <stdio.h>

int main(void) {
    FILE *fp = fopen("align_sample.txt", "w");
    if (fp == NULL) {
        fprintf(stderr, "align_sample.txt を開けませんでした。\n");
        return 1;
    }

    int x = 7;
    double y = 12.34567;
    const char *name1 = "Bob";
    const char *name2 = "Alexander";

    /* 整数の幅指定 (右寄せ) */
    fprintf(fp, "x (幅2) : '%2d'\n", x);
    fprintf(fp, "x (幅4) : '%4d'\n", x);

    /* 浮動小数点の桁数指定 */
    fprintf(fp, "y(そのまま)   : '%f'\n", y);
    fprintf(fp, "y(小数2桁)    : '%.2f'\n", y);
    fprintf(fp, "y(幅8,小数3桁): '%8.3f'\n", y);

    /* 文字列の右寄せ・左寄せ比較 */
    fprintf(fp, "name1 右寄せ(幅10): '%10s'\n", name1);
    fprintf(fp, "name1 左寄せ(幅10): '%-10s'\n", name1);
    fprintf(fp, "name2 右寄せ(幅10): '%10s'\n", name2);

    fclose(fp);
    return 0;
}

出力イメージを簡単に示すと、次のようになります。

実行結果
x (幅2) : ' 7'
x (幅4) : '   7'
y(そのまま)   : '12.345670'
y(小数2桁)    : '12.35'
y(幅8,小数3桁): '  12.346'
name1 右寄せ(幅10): '       Bob'
name1 左寄せ(幅10): 'Bob       '
name2 右寄せ(幅10): 'Alexander'

このように、数値や文字列をきれいな表形式で出力したいときに、幅や桁数の指定が非常に役立ちます。

エスケープシーケンス(\n・\tなど)とfprintf

fprintfの書式文字列では、エスケープシーケンスも使えます。

代表的なものは次の通りです。

記号意味
\n改行行の終わりに入れる
\tタブ(水平タブ)表の区切りなどに利用
\バックスラッシュパス表記などC:\temp
"ダブルクォート文字列中に"を埋め込む

例を見てみます。

C言語
#include <stdio.h>

int main(void) {
    FILE *fp = fopen("escape_sample.txt", "w");
    if (fp == NULL) {
        fprintf(stderr, "escape_sample.txt を開けませんでした。\n");
        return 1;
    }

    /* \n で改行を入れる */
    fprintf(fp, "1行目\n2行目\n3行目\n");

    /* \t でタブ区切りのような表を作る */
    fprintf(fp, "name\tage\tscore\n");
    fprintf(fp, "Bob\t20\t80\n");

    /* \" や \\ の使用例 */
    fprintf(fp, "パスの例: C:\\\\Users\\\\Public\\\\\n");
    fprintf(fp, "引用符の例: \"Hello, world!\"\n");

    fclose(fp);
    return 0;
}

特にWindows環境でパスを文字列として書くときは、バックスラッシュを2つ重ねる必要がある点に注意してください。

日本語(マルチバイト文字)をfprintfで扱う注意点

日本語をfprintfで扱う場合、マルチバイト文字であることに注意が必要です。

主なポイントは次の通りです。

  1. 文字コードとファイルの文字コード
    • 実行環境のロケールやコンパイラ設定によって、文字列リテラルのエンコーディングが変わります。
    • 多くの開発環境ではUTF-8、Windowsの一部ではShift_JISなどが使われることがあります。
    • 生成するファイルをどのアプリで開くか(Excel、メモ帳、エディタなど)を想定して、文字コードを揃えることが大切です。
  2. 文字数とバイト数の違い
    • UTF-8では、日本語1文字が3バイトになることがよくあります。
    • %sで出力するとき、strlenが返すのは「文字数」ではなく「バイト数」です。
    • 列幅をスペースで揃えようとしても、日本語を含むとずれて見えることがあります。
  3. コンソールとファイルの違い
    • コンソール表示では文字化けしても、ファイルに出力したものを対応したエディタで開くと正しく見えることもあります。
    • 逆もありえるため、必要に応じてsetlocaleを設定したり、開く側のアプリのエンコーディング設定を確認する必要があります。

簡単な例を示します。

C言語
#include <stdio.h>
#include <locale.h>

int main(void) {
    /* ロケール設定 (必要に応じて) */
    setlocale(LC_ALL, "");

    FILE *fp = fopen("japanese.txt", "w");
    if (fp == NULL) {
        fprintf(stderr, "japanese.txt を開けませんでした。\n");
        return 1;
    }

    /* 日本語文字列をそのままfprintfで書き込む例 */
    fprintf(fp, "こんにちは、fprintfの世界!\n");
    fprintf(fp, "日本語を含むテキストファイルの例です。\n");

    fclose(fp);
    return 0;
}

このコードで生成したファイルを、エディタ側をUTF-8として開けば正しく表示されます。

もし文字化けする場合は、コンパイルオプション・ソースコードの文字コード・エディタの設定をそろえることを検討してください。

fprintfを安全に使うためのポイントと応用

fprintfの戻り値とエラー処理の基本

fprintfは、成功すると書き込んだ文字数(バイト数)を返します。

エラーが起きた場合は-1が返されるため、戻り値をチェックすることで書き込みエラーを検知できます。

簡単なコード例を示します。

C言語
#include <stdio.h>

int main(void) {
    FILE *fp = fopen("log.txt", "w");
    if (fp == NULL) {
        fprintf(stderr, "log.txt を開けませんでした。\n");
        return 1;
    }

    int ret = fprintf(fp, "ログメッセージ: %s\n", "テスト書き込み");
    if (ret < 0) {
        /* 書き込みエラー時の処理 */
        fprintf(stderr, "ログへの書き込みに失敗しました。\n");
        fclose(fp);
        return 1;
    }

    fclose(fp);
    return 0;
}

ディスクがいっぱいで書けない場合や、ネットワークドライブへの書き込みに失敗した場合など、戻り値をチェックしておけば異常を検知できます。

ファイルが開けない時の対処

fopenがNULLを返した場合は、何らかの理由でファイルが開けなかったことを意味します。

よくある原因としては次のようなものがあります。

  • 指定したパスが存在しない(ディレクトリが間違っている)
  • 実行ユーザーに書き込み権限がない
  • 別のプロセスがロックしていて開けない
  • ディスクが存在しない(ネットワーク切断など)

この場合、fprintfを呼ぶ前に必ずNULLチェックを行って中断する必要があります。

C言語
#include <stdio.h>

int main(void) {
    const char *path = "C:/not_exist_dir/output.txt";

    FILE *fp = fopen(path, "w");
    if (fp == NULL) {
        /* ここで原因を示すメッセージを出す */
        fprintf(stderr, "ファイルを開けませんでした: %s\n", path);
        /* 必要であれば、別のパスに書き込む処理やリトライ処理を行う */
        return 1;
    }

    fprintf(fp, "正常に開けた場合だけ、ここに到達します。\n");

    fclose(fp);
    return 0;
}

NULLチェックを省略したままfprintfを呼ぶと、未定義動作となりクラッシュの原因になります。

ファイルを扱うときの基本的な安全対策として、fopenの直後に必ずNULL判定を書く習慣をつけると良いです。

バイナリファイルにはfprintfを使わない理由

fprintfはテキスト(文字列)を扱うための関数です。

そのため、バイナリファイルへの書き込みには向いていません

バイナリファイルとは、画像ファイル、実行ファイル、構造体をそのまま保存したファイルなど、人間が直接読むことを前提としていないファイル形式のことです。

このようなファイルには、次のような理由からfprintfではなくfwriteなどを使うべきです。

  • fprintfはフォーマット指定子に従って文字列に変換してから書き込みます。
    例えばintなら「123」を'1''2''3'という文字として出力します。一方バイナリでは、intそのもののビット列(多くの環境で4バイト)を書き込みたいはずです。
  • \nの扱いなどがテキストモード特有の変換を受けることがあります。
    Windowsでは、\n\r\nに置き換えられるなど、環境依存の変換が入ります。
    バイナリファイルではビット列が1ビットでもずれると壊れてしまいます。

こうした理由から、バイナリデータはfwrite/fread、テキストデータはfprintf/fscanfという使い分けをするのが基本です。

実用的なサンプルコード

最後に、fprintfを使った実用的なサンプルとして、簡易ログ出力プログラムとCSV形式の出力プログラムを紹介します。

サンプル1: ログファイルへの追記

C言語
#include <stdio.h>
#include <time.h>

/* 現在時刻を "YYYY-MM-DD HH:MM:SS" 形式の文字列にして返すヘルパー関数 */
static void get_timestamp(char *buf, size_t size) {
    time_t t = time(NULL);
    struct tm *tm_info = localtime(&t);

    /* strftimeで安全に文字列化する */
    strftime(buf, size, "%Y-%m-%d %H:%M:%S", tm_info);
}

/* ログメッセージを log.txt に追記する関数 */
void write_log(const char *level, const char *message) {
    FILE *fp = fopen("log.txt", "a");  /* 追記モードで開く */
    if (fp == NULL) {
        /* ログファイル自体が開けないときは標準エラー出力に出す */
        fprintf(stderr, "log.txt を開けませんでした。メッセージ: %s\n", message);
        return;
    }

    char timestamp[32];
    get_timestamp(timestamp, sizeof(timestamp));

    /* 形式: [時刻] [レベル] メッセージ */
    int ret = fprintf(fp, "[%s] [%s] %s\n", timestamp, level, message);
    if (ret < 0) {
        fprintf(stderr, "ログへの書き込みに失敗しました。\n");
    }

    fclose(fp);
}

int main(void) {
    write_log("INFO", "アプリケーションを開始しました。");
    write_log("WARN", "設定ファイルが見つかりません。デフォルト設定を使用します。");
    write_log("ERROR", "データベース接続に失敗しました。");
    write_log("INFO", "アプリケーションを終了します。");

    return 0;
}

このプログラムを実行するたびに、log.txtの末尾にログ行が追加されていきます。

追記モード"a"を使っているため、既存のログは消されません。

サンプル2: CSVファイルへの出力

次は、簡単なデータをCSV形式で出力する例です。

CSVは、カンマ,で値を区切るテキスト形式のファイルで、Excelなどで開くことができます。

C言語
#include <stdio.h>

typedef struct {
    int id;
    const char *name;
    int age;
    double score;
} Student;

int main(void) {
    Student students[] = {
        {1, "Yamada", 20, 88.5},
        {2, "Suzuki", 21, 92.0},
        {3, "Tanaka", 19, 76.25},
    };
    int num_students = (int)(sizeof(students) / sizeof(students[0]));

    FILE *fp = fopen("students.csv", "w");
    if (fp == NULL) {
        fprintf(stderr, "students.csv を開けませんでした。\n");
        return 1;
    }

    /* ヘッダー行を書き込む */
    fprintf(fp, "id,name,age,score\n");

    /* 各学生のレコードを書き込む */
    for (int i = 0; i < num_students; i++) {
        fprintf(fp, "%d,%s,%d,%.2f\n",
                students[i].id,
                students[i].name,
                students[i].age,
                students[i].score);
    }

    fclose(fp);
    return 0;
}

生成されるstudents.csvは次のような内容になります。

実行結果
id,name,age,score
1,Yamada,20,88.50
2,Suzuki,21,92.00
3,Tanaka,19,76.25

このファイルをExcelなどで開けば、各項目が列に分かれた表として表示されます。

fprintfでCSVを出力するパターンは、業務アプリケーションや分析用ツールなどで非常によく使われます。

まとめ

fprintfは「書式付き出力を任意のファイルストリームへ送る」ための関数であり、printfとほぼ同じ感覚で使える一方、出力先を柔軟に選べる点が大きな特徴です。

本文では、fopen/fcloseと組み合わせた基本的な利用手順、フォーマット指定子や桁数・配置調整、エスケープシーケンス、日本語を扱う際の注意点、戻り値によるエラー処理、バイナリとの使い分け、そしてログ出力やCSV出力といった実践的なサンプルまで解説しました。

これらを押さえておけば、コンソール表示から一歩進んだ「ファイルに残すC言語プログラム」を安心して書けるようになります。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!