C言語で ミニ日記アプリを作ろう!(5)

スポンサーリンク
C言語

前回は、メニュー方式で「過去2件表示」「新規日記入力」「終了」の3機能を選べる Ver.4を作成した。

今回は、このVer.4 をベースにして、「月ごとの一覧表示」ができるようにしたVer.5を作成していこう。
具体的には、diary.txt から読み取ったすべての記録を、タイムスタンプの年月(例:2025-08)ごとに分類して、一覧表示する。(表示は古い順)

// ミニ日記アプリ ver.5(メニュー+月ごとの一覧付き)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <time.h>

#define MAX_INPUT 1024
#define MAX_ENTRIES 1000

typedef struct {
    char timestamp[128];
    char content[MAX_INPUT];
} DiaryEntry;

// --- メニュー表示 ---
void show_menu(void) {
    printf("\n********** MENU **********\n");
    printf("1. 過去2件の日記を表示する\n");
    printf("2. 新しい日記を書く\n");
    printf("3. 月ごとの一覧を表示する\n");
    printf("9. 終了する\n");
    printf("***************************\n");
    printf("選択した番号を入力してください:");
}

// --- ファイル読み込み ---
int load_entries(const char *filename, DiaryEntry entries[], int max_entries) {
    FILE *fp = fopen(filename, "r");
    if (!fp) {
        return 0;
    }

    char line[MAX_INPUT];
    int count = 0;
    DiaryEntry temp = {"", ""};

    while (fgets(line, sizeof(line), fp)) {
        if (line[0] == '[') {
            if (strlen(temp.timestamp) > 0) {
                if (count < max_entries) {
                    entries[count++] = temp;
                }
                temp.timestamp[0] = '\0';
                temp.content[0] = '\0';
            }
            line[strcspn(line, "\n")] = '\0';
            strncpy(temp.timestamp, line + 1, sizeof(temp.timestamp) - 1);
        } else if (strlen(temp.timestamp) > 0 && line[0] != '\n') {
            line[strcspn(line, "\n")] = '\0';
            strncpy(temp.content, line, sizeof(temp.content) - 1);
        }
    }

    if (strlen(temp.timestamp) > 0) {
        if (count < max_entries) {
            entries[count++] = temp;
        }
    }

    fclose(fp);
    return count;
}

// --- 過去2件表示 ---
void show_last_two_entries(void) {
    DiaryEntry entries[MAX_ENTRIES];
    int entry_count = load_entries("diary.txt", entries, MAX_ENTRIES);

    printf("\n【過去2件の記録】\n");
    if (entry_count == 0) {
        printf("なし\n");
        return;
    }
    int start = (entry_count >= 2) ? entry_count - 2 : 0;
    for (int i = start; i < entry_count; i++) {
        printf("[%s]\n%s\n\n", entries[i].timestamp, entries[i].content);
    }
}

// --- 新規日記作成 ---
void write_new_entry(void) {
    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);

    printf("今日の日記を入力してください(Enterで終了):\n");
    if (!fgets(diary, sizeof(diary), stdin)) {
        fprintf(stderr, "入力に失敗しました。\n");
        return;
    }
    size_t len = strlen(diary);
    if (len > 0 && diary[len - 1] == '\n') {
        diary[len - 1] = '\0';
    }

    if (strlen(diary) == 0) {
        printf("入力が空です。保存しません。\n");
        return;
    }

    FILE *fp = fopen("diary.txt", "a");
    if (!fp) {
        perror("ファイルを開けませんでした");
        return;
    }
    fprintf(fp, "[%s]\n%s\n\n", timestamp, diary);
    fclose(fp);

    printf("日記を保存しました。\n");
}

// --- 月ごとの一覧表示 ---
void show_monthly_list(void) {
    DiaryEntry entries[MAX_ENTRIES];
    int entry_count = load_entries("diary.txt", entries, MAX_ENTRIES);

    if (entry_count == 0) {
        printf("\n記録はありません。\n");
        return;
    }

    printf("\n【月ごとの一覧】\n");

    char current_month[8] = "";
    for (int i = 0; i < entry_count; i++) {
        char month[8];
        strncpy(month, entries[i].timestamp, 7); // "YYYY-MM"
        month[7] = '\0';

        if (strcmp(current_month, month) != 0) {
            strcpy(current_month, month);
            printf("\n=== %s ===\n", current_month);
        }
        printf("[%s] %s\n", entries[i].timestamp, entries[i].content);
    }
}

// --- メイン ---
int main(void) {
    setlocale(LC_ALL, "ja_JP.UTF-8");

    while (1) {
        show_menu();

        char choice[10];
        if (!fgets(choice, sizeof(choice), stdin)) {
            printf("入力エラーです。\n");
            continue;
        }

        int opt = atoi(choice);

        switch (opt) {
            case 1:
                show_last_two_entries();
                break;
            case 2:
                write_new_entry();
                break;
            case 3:
                show_monthly_list();
                break;
            case 9:
                printf("終了します。\n");
                return 0;
            default:
                printf("無効な選択肢です。\n");
        }
    }
}

Ver.4 から Ver.5 への追加・変更点

項目Ver.4Ver.5
新機能月ごとの一覧表示機能追加
メニュー項目1, 2, 91, 2, 3, 9(3が新機能
表示内容過去2件、新規入力のみ月ごとに全件を分類して表示(古い順)
コード構成関数:show_last_two_entries, write_new_entry関数追加show_monthly_list
ファイル処理共通の load_entries を利用同じく利用して分類処理を追加

実行例(月ごとの一覧を表示する)

【月ごとの一覧】

=== 2025-08 ===
[2025-08-05(火) 10:00:00] 夏休みの自由研究をした。
[2025-08-10(日) 15:30:00] プールへ行った。

=== 2025-09 ===
[2025-09-01(月) 09:00:00] 新学期が始まった

コメント

タイトルとURLをコピーしました