先回は、とりあえず、簡単な「ミニ日記アプリ」を作ってみた。
今回は、下記の機能を追加したVer.2を作っていこう。
目標:曜日を表示(日本語対応)
さらに、細かな修正も行った。
//ミニ日記アプリver.2(曜日付き)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <time.h>
#define MAX_INPUT 1024
int main(void)
{
// ロケールをUTF-8に設定(MSYS2/UCRT64 なら ja_JP.UTF-8 が有効)
setlocale(LC_ALL, "ja_JP.UTF-8");
char diary[MAX_INPUT];
time_t now = time(NULL);
struct tm *t = localtime(&now);
// 曜日を取得(日本語)
const char *weekdays_jp[] = {"日", "月", "火", "水", "木", "金", "土"};
char timestamp[128];
//曜日(日本語)を含めた表示が可能
snprintf(timestamp, sizeof(timestamp),
"%04d-%02d-%02d(%s) %02d:%02d:%02d",
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
weekdays_jp[t->tm_wday],
t->tm_hour, t->tm_min, t->tm_sec);
// 入力受付(入力は1行のみ)
printf("今日の日記を入力してください(Enterで終了):\n");
if (fgets(diary, sizeof(diary), stdin) == NULL) {
fprintf(stderr, "入力に失敗しました。\n");
return 1;
}
// 改行を取り除く
size_t len = strlen(diary);
if (len > 0 && diary[len - 1] == '\n') {
diary[len - 1] = '\0';
}
// 空文字列ならスキップ
if (strlen(diary) == 0) {
printf("入力が空です。保存しません。\n");
return 0;
}
// UTF-8で追記(BOMなし)
FILE *fp = fopen("diary.txt", "a");
if (fp == NULL) {
perror("ファイルを開けませんでした");
return 1;
}
fprintf(fp, "[%s]\n%s\n\n", timestamp, diary);
fclose(fp);
printf("日記を保存しました。\n");
return 0;
}
実行例
[2025-08-08(金) 10:11:53]
今日はお天気になりました。
このコードの特徴
特徴 | 内容 |
ロケール | “ja_JP.UTF-8” に設定(MSYS2で正しく日本語処理) |
曜日表示 | 日本語で (月) のように出力 |
ファイル保存 | BOMなし UTF-8、catでもOK |
Windows互換 | VSCodeターミナルで動作確認済み |
依存ライブラリ | 標準ライブラリ (stdio.h, locale.h, time.h など) のみ |
strftime (オリジナル(ver.1))と、 snprintf (Ver.2)の違い
以下に、オリジナル(Ver.1)の strftime を使ったコードと、Ver.2の snprintf を使ったコードの違いと、それぞれの意味を解説しよう。
オリジナル(Ver.1)
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", t);
解説
- strftime は、struct tm 型の日時情報 t を指定したフォーマット文字列に従って文字列に変換する関数である。
- フォーマット文字列 “%Y-%m-%d %H:%M:%S” は以下の意味となる。
- %Y:西暦(例:2025)
- %m:月(01〜12)
- %d:日(01〜31)
- %H:時(00〜23)
- %M:分(00〜59)
- %S:秒(00〜59)
特徴
- 標準関数で簡潔。
- 曜日などの追加情報は含まれない。
Ver.2
snprintf(timestamp, sizeof(timestamp),
"%04d-%02d-%02d(%s) %02d:%02d:%02d",
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
weekdays_jp[t->tm_wday],
t->tm_hour, t->tm_min, t->tm_sec);
解説
- snprintf は、指定したフォーマットに従って文字列を生成する関数である。
- t->tm_year + 1900:tm_year は1900年からの年数なので、実際の西暦にするには1900を加算。
- t->tm_mon + 1:tm_mon は0〜11なので、1を加えて1〜12の月に変換。
- weekdays_jp[t->tm_wday]:曜日を日本語で表示するための配列(例:{“日”, “月”, “火”, “水”, “木”, “金”, “土”})から取得。
- tm_hour, tm_min, tm_sec:時・分・秒。
特徴
- 曜日(日本語)を含めた表示が可能。
- フォーマットを自由にカスタマイズできる。
- 日本語表記や装飾(例:括弧)を含められる。
比較まとめ
項目 | strftime | snprintf |
曜日表示 | ❌ 不可(別途処理必要) | ✅ 日本語で可能 |
カスタマイズ性 | 🔽 制限あり | 🔼 自由度高い |
標準関数 | ✅ | ✅ |
実装の手軽さ | ✅ | 🔽 やや複雑 |
補足
weekdays_jp の定義例:
const char *weekdays_jp[] = {“日”, “月”, “火”, “水”, “木”, “金”, “土”};
char timestamp[100]; (オリジナル(Ver.1))とchar timestamp[128];(Ver.2 )の違い
オリジナル(Ver.1) char timestamp[100]; とVer.2 char timestamp[128];の違いを解説しよう。
オリジナル(Ver.1):char timestamp[100];
これは、timestamp という文字列バッファを 100バイトの固定サイズ で定義している。
特徴
- 100バイトまでの文字列を格納可能。
- 通常の日時フォーマット(例:2025-08-06 13:45:30)には十分なサイズ。
- ただし、曜日や日本語などを追加すると足りなくなる可能性がある。
Ver.2:char timestamp[128];
こちらは、バッファサイズを 128バイトに拡張したものである。
特徴
- より長い文字列(例:2025-08-06(水) 13:45:30 のような日本語や装飾付き)にも対応可能。
- バッファオーバーフローのリスクを減らす目的。
- 可読性や安全性の向上。
コメント