C言語基礎|日付と時刻の書き込み

「タイムスタンプを保存しましょう!」— fprintf を使用して現在の日付と時刻をファイルに書き込みます。

ファイルから読み取れるようになったら、次は「ファイルへ書く」です。
とくに日付と時刻を書き込めるようになると、ログ作成や実行履歴の記録ができて、プログラムがぐっと実用的になります。

画面に出す printf と同じ感覚で、ファイルにも出力したいですよね。
その役を担当するのが fprintf です。printf は stdout(画面)に出しますが、fprintf は出力先のストリームを選べるので、ファイルに書き込めます。
さらに time と localtime を使えば「今この瞬間」の時刻を取り出して、ファイルへ保存できます。

fprintf は「出力先を指定できる printf」

printf と fprintf の違い

関数出力先何が便利?
printfstdout(通常は画面)その場で表示できる
fprintf任意のストリーム(fp や stderr など)ファイルやエラー出力に書ける

表の説明

  • fprintf は printf の先頭に「出力先 stream」を追加した形です。
  • fp にファイルのストリームを渡せばファイルへ、stderr を渡せばエラー用に出せます。

fprintf の書式(命令の形式)

書式

項目内容
ヘッダ#include <stdio.h>
形式int fprintf(FILE *stream, const char *format, ...);
動作stream が指す先へ、format で整形した文字列を書き込む
返り値書き込んだ文字数(出力エラー時は負の値)

表の説明

  • 戻り値は「何文字出したか」です。
  • 重要なログでは、必要に応じて負の値かどうかを見てエラー処理できます。

「現在時刻」を作る流れ(time と localtime)

日付と時刻は、標準ライブラリの関数呼び出しで取得できます。

図:現在時刻の取得フロー

time(NULL)
  ↓  現在の暦時刻(time_t)
localtime(&now)
  ↓  年月日・時分秒に分解された struct tm*
tm_year / tm_mon / tm_mday / tm_hour / tm_min / tm_sec

図の説明

  • time(NULL) は「今」を time_t で返します。
  • localtime はそれを「地方時のカレンダー形式」に分解して struct tm を返します。
  • tm_year と tm_mon は表示用の補正が必要です(下の表で整理します)。

struct tm の要素(補正が必要なところがポイント)

よく使うメンバと意味

メンバ意味値の例表示用の補正
tm_year1900年からの年数126(=2026年)+1900
tm_mon月(0が1月)1(=2月)+1
tm_mday15なし
tm_hour22なし
tm_min10なし
tm_sec3なし

表の説明

  • tm_year は「2026」そのものではなく「1900からの差」なので、必ず +1900 します。
  • tm_mon は 0 始まり(0=1月)なので、必ず +1 します。
  • ここを忘れると「年が1900年台」「月が1つズレる」事故が起きます。

サンプルプログラム

ログファイルに1行でタイムスタンプを書くプログラム例です。

プロジェクト名:chap13-5-1 ソースファイル名:chap13-5-1.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

Windows の Visual Studio が実行環境の場合、以下のパスに「run_log.txt」が作成されます。
C:\Users\<ユーザー名>\source\repos\chap13-5-1\chap13-5-1

#include <stdio.h>
#include <time.h>

int main(void)
{
    /* 実行履歴としてタイムスタンプを追記する */
    FILE *fp = fopen("run_log.txt", "a");
    if (fp == NULL) {
        printf("ERROR: Failed to open run_log.txt.\n");
        return 1;
    }

    /* 現在時刻を取得して、年月日・時分秒に分解する */
    time_t now = time(NULL);
    struct tm *t = localtime(&now);

    /* 読みやすい形式で書き込む(例: 2026-02-15 22:10:03) */
    int n = fprintf(fp, "%04d-%02d-%02d %02d:%02d:%02d\n",
                    t->tm_year + 1900,
                    t->tm_mon + 1,
                    t->tm_mday,
                    t->tm_hour,
                    t->tm_min,
                    t->tm_sec);

    /* 書き込み失敗を軽くチェックする */
    if (n < 0) {
        printf("ERROR: Failed to write to the log file.\n");
        fclose(fp);
        return 1;
    }

    fclose(fp);

    printf("Timestamp saved to run_log.txt.\n");
    return 0;
}

図で理解:このプログラムがやっていること

図:タイムスタンプを書いて閉じる

fopen(run_log.txt, a)
   ↓
time(NULL) → localtime(&now)
   ↓
fprintf(fp, "YYYY-MM-DD HH:MM:SS")
   ↓
fclose(fp)
   ↓
printf で完了メッセージ

図の説明

  • a(追加)で開くので、実行するたびに run_log.txt の末尾に行が増えます。
  • fclose は「残ったバッファを書き出して閉じる」ので、最後の締めとして重要です。

printf と fprintf(stdout, ...) は同じ感覚でOK

標準出力ストリーム stdout は FILE* なので、fprintf の第1引数にそのまま渡せます。

同じ働きになる例

やりたいこと書き方A書き方B
画面へ整数を出すprintf("%d", x);fprintf(stdout, "%d", x);
キーボードから整数を読むscanf("%d", &x);fscanf(stdin, "%d", &x);

表の説明

  • printf は「出力先が stdout 固定の fprintf」と考えると理解が一気に楽になります。
  • 同様に scanf は「入力元が stdin 固定の fscanf」です。

よくあるつまずきポイント

ありがちなミスと対策

ミス起きること対策
fopen の失敗をチェックしないfp が NULL のまま fprintf して危険if (fp == NULL) で分岐
tm_year をそのまま使う年が 1900 ずれる+1900
tm_mon をそのまま使う月が 1 ずれる+1
fclose を忘れる書いたはずの内容が残らないことがある最後に必ず fclose

表の説明

  • とくに fclose 忘れは「たまに起きる」ので厄介です。習慣で潰すのが一番です。

演習問題

演習13-4:複数人のデータをファイルへ書く

キーボードから複数人分の 名前 身長 体重 を読み込み、people.txt に空白区切りで書き込め。
入力終了は名前に end が入力されたときとする。保存形式は 名前 身長 体重 の順とする。

解答例

プロジェクト名:chap13-5-2 ソースファイル名:chap13-5-2.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

Windows の Visual Studio が実行環境の場合、以下のパスに「people.txt」が作成されます。
C:\Users\<ユーザー名>\source\repos\chap13-5-2\chap13-5-2

#include <stdio.h>

int main(void)
{
    /* 人のデータを保存するファイルを作る(既存なら中身は消える) */
    FILE *fp = fopen("people.txt", "w");
    if (fp == NULL) {
        printf("ERROR: Cannot open people.txt.\n");
        return 1;
    }

    char name[100];
    double height, weight;

    printf("Enter records. Type end to finish.\n");

    while (1) {
        printf("Name: ");
        if (scanf("%99s", name) != 1) {
            printf("ERROR: Input failed.\n");
            break;
        }

        /* end なら終了 */
        if (name[0]=='e' && name[1]=='n' && name[2]=='d' && name[3]=='\0')
            break;

        printf("Height Weight: ");
        if (scanf("%lf%lf", &height, &weight) != 2) {
            printf("ERROR: Invalid numbers.\n");
            break;
        }

        /* ファイルへ書き込む(名前 身長 体重) */
        fprintf(fp, "%s %.1f %.1f\n", name, height, weight);
    }

    fclose(fp);
    printf("Saved to people.txt.\n");
    return 0;
}

解説

  • fprintf(fp, ...) を使うことで「画面ではなくファイルへ出力」できます。
  • w で開くので、people.txt は毎回まっさらから作り直されます。
  • scanf の戻り値チェックで入力の失敗を検出できます。
  • end を合図にしてループを止めると、入力回数を自由にできます。