
C言語基礎|前回実行時の情報を取得
「前回いつ実行したかが分かるだけで、プログラムは“道具”になる。」
プログラムは終了するとメモリの中身が消えてしまいます。だからこそ、前回実行したときの情報を覚えておけると、とても便利になります。
たとえば「前回いつ実行したか」が分かれば、ログの起点になったり、更新タイミングの判断に使えたりします。
この節では、前回実行時の日付と時刻をファイルに保存しておき、次回起動時にそれを読み取って表示する方法を、やさしく丁寧に身につけていきます。
まず全体像:起動時に読む、終了時に書く
図:前回時刻の引き継ぎフロー
起動
↓
datetime.dat を読み取りで開く(r)
↓
読めたら「前回時刻」を表示 / 開けなければ「初回」を表示
↓
現在時刻を取得(time → localtime)
↓
datetime.dat を書き込みで開く(w)
↓
今回時刻を書き込む(fprintf)
↓
終了
図の説明
- r で開けないなら、保存ファイルが無い=初回実行の可能性が高い、という判断ができます。
- 終了時に w で保存しておくと、次回起動時にそれが「前回情報」になります。
- “読む→表示→書く”の順番が基本の型です。
サンプルプログラム(前回の日付と時刻を表示)
「前回の日付と時刻」を表示するプログラム例です。
保存形式は、読み書きしやすいように「年 月 日 時 分 秒」をスペース区切りで1行にします。
プロジェクト名:chap13-7-1 ソースファイル名:chap13-7-1.c
Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。
Windows の Visual Studio が実行環境の場合、以下のパスに「datetime.dat」が作成されます。
C:\Users\<ユーザー名>\source\repos\chap13-7-1\chap13-7-1
#include <time.h>
#include <stdio.h>
char data_file[] = "datetime.dat"; /* 保存ファイル名 */
/* 前回の日付・時刻を取得して表示する */
void show_previous_datetime(void)
{
FILE *fp = fopen(data_file, "r");
if (fp == NULL) {
/* 保存ファイルが無い=初回実行の可能性が高い */
printf("This looks like the first run.\n");
return;
}
int year, month, day, hour, min, sec;
/* 6項目を読み取れたかで判定する */
if (fscanf(fp, "%d%d%d%d%d%d", &year, &month, &day, &hour, &min, &sec) == 6) {
printf("Last run was at %04d-%02d-%02d %02d:%02d:%02d.\n",
year, month, day, hour, min, sec);
} else {
printf("Previous data is missing or broken.\n");
}
fclose(fp); /* クローズ */
}
/* 今回の日付・時刻を書き込む(次回の前回情報になる) */
void save_current_datetime(void)
{
FILE *fp = fopen(data_file, "w");
if (fp == NULL) {
printf("ERROR: Cannot open datetime.dat for writing.\n");
return;
}
/* 現在の暦時刻を取得して、地方時の要素別時刻に変換する */
time_t current = time(NULL);
struct tm *t = localtime(¤t);
if (t == NULL) {
printf("ERROR: Failed to get local time.\n");
fclose(fp);
return;
}
/* 年 月 日 時 分 秒 を保存する(スペース区切り) */
fprintf(fp, "%d %d %d %d %d %d\n",
t->tm_year + 1900, /* 年は1900年からの差なので補正 */
t->tm_mon + 1, /* 月は0始まりなので補正 */
t->tm_mday, /* 日 */
t->tm_hour, /* 時 */
t->tm_min, /* 分 */
t->tm_sec /* 秒 */
);
fclose(fp); /* クローズ */
}
int main(void)
{
show_previous_datetime(); /* 前回情報を表示 */
save_current_datetime(); /* 今回情報を保存 */
return 0;
}保存ファイル datetime.dat の中身(フォーマット)
このプログラムは、datetime.dat に次の形式で1行保存します。
保存形式(1行)
| 項目 | 例 | 説明 |
|---|---|---|
| 年 月 日 時 分 秒 | 2026 2 15 22 10 3 | スペース区切りの6個の整数 |
表の説明
- 読み取り側は fscanf で整数6個を取るだけなので、とても単純です。
- 表示用にゼロ埋めしたい場合は printf 側で %04d や %02d を使えばOKです。
初回と2回目以降で何が起きる?
実行回数と表示
| 実行回数 | show_previous_datetime の表示 | save_current_datetime の動作 |
|---|---|---|
| 1回目 | This looks like the first run. | datetime.dat を作って現在時刻を保存 |
| 2回目以降 | Last run was at YYYY-MM-DD HH:MM:SS. | datetime.dat を上書きして今回時刻を保存 |
表の説明
- 初回は datetime.dat が存在しないので r で開けず、初回メッセージになります。
- 2回目は前回保存した時刻が読めるので、前回時刻が表示されます。
- 保存は w なので毎回最新の時刻に更新されます。
使う命令(関数)の書式を整理
fopen
| 項目 | 内容 |
|---|---|
| ヘッダ | #include <stdio.h> |
| 形式 | FILE *fopen(const char *filename, const char *mode); |
| モード例 | r(読み取り), w(書き込み・新規/切り捨て) |
| 失敗 | NULL |
fscanf
| 項目 | 内容 |
|---|---|
| ヘッダ | #include <stdio.h> |
| 形式 | int fscanf(FILE *stream, const char *format, ...); |
| 返り値 | 成功した代入項目数(今回なら 6 が成功) |
fprintf
| 項目 | 内容 |
|---|---|
| ヘッダ | #include <stdio.h> |
| 形式 | int fprintf(FILE *stream, const char *format, ...); |
| 返り値 | 書き込んだ文字数(エラー時は負) |
fclose
| 項目 | 内容 |
|---|---|
| ヘッダ | #include <stdio.h> |
| 形式 | int fclose(FILE *stream); |
| 役割 | フラッシュして閉じる |
time
| 項目 | 内容 |
|---|---|
| ヘッダ | #include <time.h> |
| 形式 | time_t time(time_t *t); |
| 使い方例 | current = time(NULL); |
localtime
| 項目 | 内容 |
|---|---|
| ヘッダ | #include <time.h> |
| 形式 | struct tm *localtime(const time_t *timep); |
| 役割 | 暦時刻を地方時の要素別時刻に変換 |
表の説明
- 前回情報の読み取りは fopen の成否と fscanf の戻り値で判断します。
- 現在時刻は time → localtime の流れで取得します。
- tm_year と tm_mon の補正(+1900、+1)は必須です。
図で理解:なぜ tm_year と tm_mon は補正が必要?
図:struct tm の “クセ”
tm_year = 1900年からの年数 → 表示は +1900
tm_mon = 0が1月、11が12月 → 表示は +1
図の説明
- struct tm は「人間が読むための構造体」ですが、年と月だけは内部表現が独特です。
- ここを補正して表示・保存するのが定番です。
演習問題
演習13-5:前回の時刻+メモを保存する
前回実行時刻を表示した後、今回のメモ(短い文字列)を入力し、時刻と一緒に保存せよ。
次回起動時に「前回は…で、メモは…でした」と表示すること。
解答方針(保存形式)
- 1行目:年 月 日 時 分 秒
- 2行目:メモ文字列(fgetsで読む)
解答例
プロジェクト名:chap13-7-2 ソースファイル名:chap13-7-2.c
Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。
Windows の Visual Studio が実行環境の場合、以下のパスに「datetime_memo.dat」が作成されます。
C:\Users\<ユーザー名>\source\repos\chap13-7-2\chap13-7-2
#include <time.h>
#include <stdio.h>
char data_file[] = "datetime_memo.dat";
void show_previous(void)
{
FILE *fp = fopen(data_file, "r");
if (fp == NULL) {
printf("No previous record found.\n");
return;
}
int y, mo, d, h, mi, s;
char memo[200];
if (fscanf(fp, "%d%d%d%d%d%d\n", &y, &mo, &d, &h, &mi, &s) == 6 &&
fgets(memo, sizeof memo, fp) != NULL) {
printf("Last run: %04d-%02d-%02d %02d:%02d:%02d, memo: %s",
y, mo, d, h, mi, s, memo);
} else {
printf("Previous record is broken.\n");
}
fclose(fp);
}
void save_current(void)
{
/* メモ入力 */
char memo[200];
printf("Enter a memo for this run: ");
/* 入力状態によって改行が残る場合があるので掃除 */
int ch;
while ((ch = getchar()) != '\n' && ch != EOF) { }
if (fgets(memo, sizeof memo, stdin) == NULL) {
printf("ERROR: Input failed.\n");
return;
}
FILE *fp = fopen(data_file, "w");
if (fp == NULL) {
printf("ERROR: Cannot open save file.\n");
return;
}
time_t current = time(NULL);
struct tm *t = localtime(¤t);
if (t == NULL) {
printf("ERROR: Failed to get local time.\n");
fclose(fp);
return;
}
fprintf(fp, "%d %d %d %d %d %d\n",
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec);
fprintf(fp, "%s", memo);
fclose(fp);
printf("Saved.\n");
}
int main(void)
{
show_previous();
save_current();
return 0;
}