软件开发与创新——万年历功能新增与代码优化

一、项目名称与来源
上海海洋大学C语言期末大作业
二、原项目运行
运行环境:
系统:Windows11 24H2
cpu:i7-10750H
编译器:Dev c++ 5.11
运行结果:

点击查看代码
#include <stdio.h>
#include <stdlib.h>

// 定义每个月的天数,第一行为平年,第二行为闰年
int dateofmonth[2][12] = {
    {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
    {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

// 英文月份名称数组
char *month1[] = {
    "January", "February", "March", "April", "May", "June",
    "July", "August", "September", "October", "November", "December"
};

// 中文月份名称数组
char *month2[] = {
    "一月", "二月", "三月", "四月", "五月", "六月",
    "七月", "八月", "九月", "十月", "十一月", "十二月"
};

// 中文星期名称数组
char *week[7] = {"日", "一", "二", "三", "四", "五", "六"};

// 判断是否为闰年
int isLeapYear(int year) {
    return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}

// 计算指定日期是星期几
int getWeekday(int year, int month, int date) {
    int days = 0;
    int leap = isLeapYear(year);
    for (int i = 0; i < month - 1; i++) {
        days += dateofmonth[leap][i];
    }
    days += date;
    return ((year - 1) + (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400 + days) % 7;
}

// 打印指定年份的日历
void printCalendar(int year, int month_a, int month_b) {
    printf("\n\n\n\t—————————————%d 年日历—————————————\n\n\n", year);
    for (int i = month_a - 1; i < month_b; i++) {
        int firstWeekday = getWeekday(year, i + 1, 1);
        int daysInMonth = dateofmonth[isLeapYear(year)][i];

        // 打印月份名称
        printf("\t%-8s\t\t\t\t\t%6s\n", month1[i], month2[i]);
        printf("\t______________________________________________________\n\t  ");

        // 打印星期名称
        for (int j = 0; j < 7; j++) {
            printf("%s\t", week[j]);
        }
        printf("\n\n");
        printf("\t# ");

        // 打印空白以对齐第一个日期
        if (firstWeekday > 0) {
            for (int n = 0; n < firstWeekday; n++) {
                printf("  \t");
            }
            printf(" ");
        }

        // 打印日期
        for (int k = 1; k <= daysInMonth; k++) {
            if (firstWeekday == 7) {
                printf("\n\t# ");
                firstWeekday = 0;
            }
            if (firstWeekday == 6) {
                printf("%2d #", k);
            } else {
                printf("%2d\t ", k);
            }
            firstWeekday++;
        }

        // 补齐行尾
        if (firstWeekday < 6) {
            while (firstWeekday++ != 6) {
                printf("  \t");
            }
            printf("    #");
        } else if (firstWeekday == 6) {
            printf("   #");
        }
        printf("\n\t______________________________________________________\n");
        printf("\n\n\n\n\n");
    }
}

// 查询指定日期是星期几
void queryWeekday() {
    int year, month, day;
    printf("请输入要查询的日期 年 月 日(2024 1 1):");
    scanf("%d %d %d", &year, &month, &day);
    printf("\n\n查询结果如下:\n\n%d年%d月%d日是星期%s\n\n\n\n\n", year, month, day, week[getWeekday(year, month, day)]);
}

// 查询指定年份是否为闰年
void queryLeapYear() {
    int year;
    printf("请输入要查询的年份(2024):");
    scanf("%d", &year);
    printf("\n\n查询结果如下:\n\n");
    if (isLeapYear(year)) {
        printf("%d是闰年\n", year);
    } else {
        printf("%d不是闰年\n", year);
    }
}

// 查询指定月份的最大天数
void queryDaysInMonth() {
    int year, month;
    printf("请输入要查询的月份(2024 1):");
    scanf("%d %d", &year, &month);
    printf("\n\n查询结果如下:\n\n");
    int leap = isLeapYear(year);
    printf("%d年%d月有%d天\n\n", year, month, dateofmonth[leap][month - 1]);
}

// 主菜单函数
void showMenu() {
    printf("         万年历查询系统\n\n");
    printf("**************************************\n");
    printf("1:查询某年某月某日是星期几\n");
    printf("2:查询某年是否是闰年\n");
    printf("3:打印某年的日历\n");
    printf("4.查询某月的最大天数\n");
    printf("5:退出\n");
    printf("**************************************\n");
    printf("\n请选择: ");
}

int main() {
    int choice, year;
    while (1) {
        showMenu();
        scanf("%d", &choice);
        printf("\n");

        switch (choice) {
            case 1:
                queryWeekday();
                break;
            case 2:
                queryLeapYear();
                break;
            case 3:
                printf("请输入要打印的年份(2024):");
                scanf("%d", &year);
                printCalendar(year, 1, 12);
                break;
            case 4:
                queryDaysInMonth();
                break;
            case 5:
                return 0;
            default:
                printf("\n\n\t输入错误,请重新输入\n\n\n\n");
        }
    }
    return 0;
}

三、主要问题

  1. 代码冗余与重复
    逻辑重复:在多个函数中都存在对闰年判断的逻辑调用,如 getWeekday、printCalendar、queryDaysInMonth 等函数,虽然调用的是同一个 isLeapYear 函数,但多次调用会造成一定的代码冗余。
    2.错误处理
    输入验证:代码中缺乏对用户输入的验证,例如在 queryWeekday、queryLeapYear 等函数中,用户输入的日期可能不符合实际情况(如月份超出 1 - 12 的范围),但代码没有进行相应的验证和处理,可能会导致程序出现异常。
    3.可读性与可维护性
    变量命名:部分变量命名不够清晰,例如在 getWeekday 函数中,days 变量虽然能表达其存储的是天数,但没有明确说明是从哪个时间点开始计算的天数,降低了代码的可读性。
    注释不足:虽然代码中有一些注释,但整体注释不够详细,特别是一些关键逻辑部分,如计算星期几的公式,没有详细解释,不利于后续的维护和扩展。
    代码结构:代码整体结构较为复杂,函数内部的逻辑较多,例如 printCalendar 函数中包含了打印月份名称、星期名称、日期以及处理节假日等多个功能,代码过长,可将一些功能拆分成更小的函数,提高代码的可维护性。
    四、优化
    1.添加节假日显示
点击查看代码
// 打印指定年份的日历
void printCalendar(int year, int month_a, int month_b) {
    printf("\n\n\n\t—————————————%d 年日历—————————————\n\n\n", year);
    for (int i = month_a - 1; i < month_b; i++) {
        int firstWeekday = getWeekday(year, i + 1, 1);
        int daysInMonth = dateofmonth[isLeapYear(year)][i];

        // 打印月份名称
        printf("\t%-8s\t\t\t\t\t%6s\n", month1[i], month2[i]);
        printf("\t______________________________________________________\n\t  ");

        // 打印星期名称
        for (int j = 0; j < 7; j++) {
            printf("%s\t", week[j]);
        }
        printf("\n\n");
        printf("\t# ");

        // 打印空白以对齐第一个日期
        if (firstWeekday > 0) {
            for (int n = 0; n < firstWeekday; n++) {
                printf("  \t");
            }
            printf(" ");
        }

        // 打印日期
        for (int k = 1; k <= daysInMonth; k++) {
            if (firstWeekday == 7) {
                printf("\n\t# ");
                firstWeekday = 0;
            }
            int isHoliday = 0;
            for (int h = 0; h < sizeof(holidays) / sizeof(holidays[0]); h++) {
                if (holidays[h][0] == i + 1 && holidays[h][1] == k) {
                    isHoliday = 1;
                    printf("\033[31m%2d(%s)\033[0m\t ", k, holiday_names[holidays[h][2] - 1]);
                    break;
                }
            }
            if (!isHoliday) {
                if (firstWeekday == 6) {
                    printf("%2d #", k);
                } else {
                    printf("%2d\t ", k);
                }
            }
            firstWeekday++;
        }

        // 补齐行尾
        if (firstWeekday < 6) {
            while (firstWeekday++ != 6) {
                printf("  \t");
            }
            printf("    #");
        } else if (firstWeekday == 6) {
            printf("   #");
        }
        printf("\n\t______________________________________________________\n");
        printf("\n\n\n\n\n");
    }
}

2.添加生肖和星座查询

点击查看代码
// 查询指定日期是星期几
void queryWeekday() {
    int year, month, day;
    printf("请输入要查询的日期 年 月 日(2024 1 1):");
    scanf("%d %d %d", &year, &month, &day);
    printf("\n\n查询结果如下:\n\n%d年%d月%d日是星期%s\n", year, month, day, week[getWeekday(year, month, day)]);

    // 显示星座
    int constellation_index;
    if ((month == 1 && day >= 20) || (month == 2 && day <= 18)) {
        constellation_index = 1;
    } else if ((month == 2 && day >= 19) || (month == 3 && day <= 20)) {
        constellation_index = 2;
    } else if ((month == 3 && day >= 21) || (month == 4 && day <= 19)) {
        constellation_index = 3;
    } else if ((month == 4 && day >= 20) || (month == 5 && day <= 20)) {
        constellation_index = 4;
    } else if ((month == 5 && day >= 21) || (month == 6 && day <= 20)) {
        constellation_index = 5;
    } else if ((month == 6 && day >= 21) || (month == 7 && day <= 22)) {
        constellation_index = 6;
    } else if ((month == 7 && day >= 23) || (month == 8 && day <= 22)) {
        constellation_index = 7;
    } else if ((month == 8 && day >= 23) || (month == 9 && day <= 22)) {
        constellation_index = 8;
    } else if ((month == 9 && day >= 23) || (month == 10 && day <= 22)) {
        constellation_index = 9;
    } else if ((month == 10 && day >= 23) || (month == 11 && day <= 21)) {
        constellation_index = 10;
    } else if ((month == 11 && day >= 22) || (month == 12 && day <= 21)) {
        constellation_index = 11;
    } else {
        constellation_index = 0;
    }
    printf("星座是:%s\n", constellations[constellation_index]);

    // 显示生肖
    int zodiac_index = (year - 1900) % 12;
    printf("生肖是:%s\n\n\n\n", zodiacs[zodiac_index]);
}

3.倒计时功能

点击查看代码
// 倒计时功能
void countdown() {
    int year1, month1, day1, year2, month2, day2;
    printf("请输入当前日期 年 月 日(2024 1 1):");
    scanf("%d %d %d", &year1, &month1, &day1);
    printf("请输入目标日期 年 月 日(2024 1 1):");
    scanf("%d %d %d", &year2, &month2, &day2);

    int days1 = 0, days2 = 0;
    for (int y = 1900; y < year1; y++) {
        days1 += isLeapYear(y) ? 366 : 365;
    }
    int leap1 = isLeapYear(year1);
    for (int m = 0; m < month1 - 1; m++) {
        days1 += dateofmonth[leap1][m];
    }
    days1 += day1;

    for (int y = 1900; y < year2; y++) {
        days2 += isLeapYear(y) ? 366 : 365;
    }
    int leap2 = isLeapYear(year2);
    for (int m = 0; m < month2 - 1; m++) {
        days2 += dateofmonth[leap2][m];
    }
    days2 += day2;

    int diff = days2 - days1;
    if (diff > 0) {
        printf("\n距离 %d年%d月%d日 还有 %d 天\n\n\n", year2, month2, day2, diff);
    } else if (diff < 0) {
        printf("\n %d年%d月%d日 已经过去 %d 天\n\n\n", year2, month2, day2, -diff);
    } else {
        printf("\n今天就是 %d年%d月%d日\n\n\n", year2, month2, day2);
    }
}

4.对代码的优化和精简

点击查看代码
#include <stdio.h>

// 每月天数,[0]平年 [1]闰年
int dateofmonth[2][12] = {{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
                          {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
const char *month1[] = {"January", "February", "March", "April", "May", "June",
                        "July", "August", "September", "October", "November", "December"};
const char *month2[] = {"一月", "二月", "三月", "四月", "五月", "六月",
                        "七月", "八月", "九月", "十月", "十一月", "十二月"};
const char *week[7] = {"日", "一", "二", "三", "四", "五", "六"};
const int holidays[][3] = {{1, 1, 1}, {2, 14, 2}, {3, 8, 3}, {4, 1, 4}, {5, 1, 5},
                           {6, 1, 6}, {9, 10, 7}, {10, 1, 8}, {12, 25, 9}};
const char *holiday_names[] = {"元旦", "情人节", "妇女节", "愚人节", "劳动节",
                               "儿童节", "教师节", "国庆节", "圣诞节"};
const char *constellations[] = {"摩羯座", "水瓶座", "双鱼座", "白羊座", "金牛座", "双子座",
                                "巨蟹座", "狮子座", "处女座", "天秤座", "天蝎座", "射手座", "摩羯座"};
const int constellation_bounds[12] = {20, 19, 21, 20, 21, 22, 23, 23, 23, 24, 23, 22};
const char *zodiacs[] = {"鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪"};

// 判断闰年
int isLeapYear(int y) { return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0); }

// 计算星期
int getWeekday(int y, int m, int d) {
    int days = d;
    for (int i = 0; i < m - 1; i++) days += dateofmonth[isLeapYear(y)][i];
    return (y - 1 + (y - 1) / 4 - (y - 1) / 100 + (y - 1) / 400 + days) % 7;
}

// 打印日历
void printCalendar(int y) {
    printf("\n\n\n\t—————————————%d 年日历—————————————\n\n\n", y);
    for (int m = 0; m < 12; m++) {
        int first = getWeekday(y, m + 1, 1), days = dateofmonth[isLeapYear(y)][m];
        printf("\t%-8s\t\t\t\t\t%6s\n", month1[m], month2[m]);
        printf("\t______________________________________________________\n\t  ");
        for (int i = 0; i < 7; i++) printf("%s\t", week[i]);
        printf("\n\n\t# ");
        for (int i = 0; i < first; i++) printf("  \t");
        for (int d = 1; d <= days; d++) {
            if ((first + d - 1) % 7 == 0) printf("\n\t# ");
            int isHoliday = 0;
            for (int h = 0; h < sizeof(holidays) / sizeof(holidays[0]); h++)
                if (holidays[h][0] == m + 1 && holidays[h][1] == d) {
                    isHoliday = 1;
                    printf("\033[31m%2d(%s)\033[0m\t ", d, holiday_names[holidays[h][2] - 1]);
                    break;
                }
            if (!isHoliday) printf("%2d\t ", d);
        }
        printf("\n\t______________________________________________________\n\n\n\n\n");
    }
}

// 查询星期、星座、生肖
void queryWeekday() {
    int y, m, d;
    printf("请输入要查询的日期 年 月 日(2024 1 1):");
    scanf("%d %d %d", &y, &m, &d);
    int w = getWeekday(y, m, d), c = (m - 1 + (d < constellation_bounds[m - 1] ? 0 : 1)) % 12, z = (y - 1900) % 12;
    printf("\n\n查询结果如下:\n\n%d年%d月%d日是星期%s\n星座是:%s\n生肖是:%s\n\n\n\n", y, m, d, week[w], constellations[c], zodiacs[z]);
}

// 查询闰年
void queryLeapYear() {
    int y;
    printf("请输入要查询的年份(2024):");
    scanf("%d", &y);
    printf("\n\n查询结果如下:\n\n%d %s闰年\n\n", y, isLeapYear(y) ? "是" : "不是");
}

// 查询月最大天数
void queryDaysInMonth() {
    int y, m;
    printf("请输入要查询的月份(2024 1):");
    scanf("%d %d", &y, &m);
    printf("\n\n查询结果如下:\n\n%d年%d月有%d天\n\n", y, m, dateofmonth[isLeapYear(y)][m - 1]);
}

// 倒计时
void countdown() {
    int y1, m1, d1, y2, m2, d2;
    printf("请输入当前日期 年 月 日(2024 1 1):");
    scanf("%d %d %d", &y1, &m1, &d1);
    printf("请输入目标日期 年 月 日(2024 1 1):");
    scanf("%d %d %d", &y2, &m2, &d2);
    int days1 = 0, days2 = 0;
    for (int y = 1900; y < y1; y++) days1 += isLeapYear(y) ? 366 : 365;
    for (int m = 0; m < m1 - 1; m++) days1 += dateofmonth[isLeapYear(y1)][m];
    days1 += d1;
    for (int y = 1900; y < y2; y++) days2 += isLeapYear(y) ? 366 : 365;
    for (int m = 0; m < m2 - 1; m++) days2 += dateofmonth[isLeapYear(y2)][m];
    days2 += d2;
    int diff = days2 - days1;
    printf("\n%s %d年%d月%d日 %s %d 天\n\n\n",
           diff > 0 ? "距离" : diff < 0 ? "" : "今天就是",
           y2, m2, d2,
           diff > 0 ? "还有" : diff < 0 ? "已经过去" : "",
           diff > 0 ? diff : -diff);
}

// 主菜单
void showMenu() {
    printf("         万年历查询系统\n\n");
    printf("**************************************\n");
    printf("1:查询某年某月某日是星期几\n");
    printf("2:查询某年是否是闰年\n");
    printf("3:打印某年的日历\n");
    printf("4:查询某月的最大天数\n");
    printf("5:倒计时功能\n");
    printf("6:退出\n");
    printf("**************************************\n");
    printf("\n请选择: ");
}

int main() {
    int choice, year;
    while (1) {
        showMenu();
        scanf("%d", &choice);
        printf("\n");
        switch (choice) {
            case 1: queryWeekday(); break;
            case 2: queryLeapYear(); break;
            case 3: printf("请输入要打印的年份(2024):"); scanf("%d", &year); printCalendar(year); break;
            case 4: queryDaysInMonth(); break;
            case 5: countdown(); break;
            case 6: return 0;
            default: printf("\n\n\t输入错误,请重新输入\n\n\n\n");
        }
    }
    return 0;
}

五、实现效果



6、总结
难点
1.节假日显示处理
难点:要在日历中准确显示节假日,需要对每个日期进行判断,并且要考虑到不同年份的情况。同时,为了增强显示效果,使用了 ANSI 转义序列来设置节假日日期的颜色,这涉及到对控制台输出格式的控制。
耗时原因:需要设计合理的数据结构来存储节假日信息,并且要编写循环逻辑来遍历每个日期并检查是否为节假日。此外,还要处理不同操作系统和终端对 ANSI 转义序列的支持情况,确保显示效果的一致性。
2.代码优化与精简
难点:在保证代码功能完整的前提下,要对代码进行优化和精简,减少代码冗余,提高代码的可读性和可维护性。这需要对代码结构有深入的理解,并且要掌握一些优化技巧,如函数封装、变量命名规范等。
耗时原因:需要反复审查代码,找出可以优化的部分,并进行多次修改和测试,以确保优化后的代码仍然能够正常工作。
思考
1.代码重构和优化
逆向工程的结果可以为代码重构和优化提供依据。通过分析代码的结构和逻辑,可以找出可以改进的部分,并进行相应的重构和优化。例如,可以将一些重复的代码提取成独立的函数,减少代码冗余;可以优化循环逻辑,提高代码的执行效率。
2.发现潜在问题
逆向工程可以帮助发现代码中存在的潜在问题。例如,在分析代码时,可以发现代码中缺乏输入验证的问题,这可能会导致程序在接收到非法输入时出现异常。此外,还可以发现代码中存在的性能问题,如重复计算、循环嵌套过深等,这些问题可能会影响程序的运行效率。

posted @ 2025-02-26 22:53  刘星玮  阅读(77)  评论(0)    收藏  举报