题目要求

编程实现万年历,要求:

可根据用户输入或系统日期进行初始化,如果用户无输入则显示系统日期所在月份的月历,并突出显示当前日期;

可根据用户输入的日期查询,并显示查询结果所在月份的月历,突出显示当前日期,并提示是否闰年

对任何不合法输入数据,拒绝查询并进行提示。

思路分析

可将思考、编程划分为以下几个模块:

如何通过已有日期和星期推算要求的日期的星期?

如何整齐地输出月历?

如何获取系统时间?

在有余力的前提下,如何美化界面?

下面对上面的几个问题给出粗略的概述。

具体实现和技巧性地东西参考后文代码。

问题1 日期推算

众所周知,需要推算日期的模拟题都是毒瘤题

日期推算的算法有很多,这里只给出我的思路:

推出差了多少天。

用数学公式推出星期。

这条公式是 (w+d)mod7(w+d)mod7 ,d 表示差的天数,w 表示原本是星期几。

我采用的是标准的 0 表示 Sun. 而 6 表示 Sat. 的方法。

time.h 自带的 tm_wday 就是用这种方式表示的。

需要注意的是 C 与 C++ 对负数取模的特(sha)殊(bi)性 ,所以为了求出正确的结果,我们要采用一点小技巧。

if(w1+d<0) w2=(w1+d)+(-w1-d)/7*7+7; 

似乎也可以在推出天数后乘上86400减一下然后扔给 localtime() 去推星期。

但是你连天数都推出来了,直接算不香吗。而且既然是万年历,秒数太大爆了怎么办

接下来让我们考虑如何推算差了多少天。

我为了方便计算,所有的推算都以2020年1月1日星期三为基准。

由一个基准来推的化可以省去很多麻烦。

首先,第一种方法是暴力模拟。一年一年地推、一月一月地推、一天一天地推。

我在代码中注释掉的就是暴力模拟法。

这个没什么好讲的,闰年就差 366 天,否则差 365 天。

年推到了就推月,实现把每个月份的天数打个表,别忘了特判二月就行。

你也可以不像我那样偷懒一个一个月推,使用 前缀和数组+闰年特判 也行。但是每次查询最多就推 12 个月,一个月一个月推也差不了多少。

这点时间肉眼是看不出来的。所以随便吧。

天数就没什么好说的,自己随便想两个同年同月的日期看看差几天,很快就能看出是直接拿日期相减了。

其实,我们不难发现,年份可以不用一年一年模拟,可以用数学公式算。

现在我们要算 A年1月1日 到 B年1月1日 经过了几个闰年。

以 A < B 为例

直接拿 (B-A)/4 来算闰年个数这种玄学的事情我是不会干的。我希望求出的闰年个数是绝对准确的。

因此可以这样来:

我们知道 x/4 可以表示小于等于 x 的正整数中 4 的倍数的个数。

我们需要求经过的闰年的个数,只需要知道区间 [A,B-1] 中 4、100、400 的倍数的个数就行了。

( 因为我考虑的是 1月1日 ,如果考虑 12月31日 的话,应该变为 [A+1,B] )

根据容斥原理,记 4、100、400 的倍数的个数分别为 c1,c2,c3c1,c2,c3

我们有: n=c1−c2+c3n=c1−c2+c3

根据 前缀和 的思想,我们有:

c1=(B−1)/4−(A−1)/4c1=(B−1)/4−(A−1)/4

应该不会有人看不懂前缀和吧,不过我还是解释一下吧。

因为 A 是包含在区间里面的,我们要求 [A,B-1] 的区间权值,自然不能把 A 删出去,所以要用 A-1 。

其它几项同理。

于是我们求出了闰年的个数,于是 d=(B−A)+n×1d=(B−A)+n×1

至于 A > B 的情形,同理,只需要把区间改为 [B,A-1] 。

然后根据前缀和,你会发现 式子是一样的,只是正负号变了而已,所以没有分类讨论的必要 。

这样就解决了最关键的问题,剩下的只需要动用知识和 耐心 去模拟就好了。

问题2 月历的格式

这个随便百度一下万年历或者点一下右下角的时间模仿一下它的格式就行了。这里介绍几个技巧。

分行 printf (这个好像谁都会)

对齐

利用 %-*d 可以靠左对齐, %*d 则是靠右对齐。

总之计算好需要的字符长度然后分配即可。看着不行多试几次。

利用字符数组减少工作量

需要注意的是,二维数组的字符串长度必须声明。因为只有知道了长度才可以分配内存。二维数组不止要分配第一个字符串的内存,还要同时按间隔分配余下的内存,不规定长度的话它不知道要在哪里放第二个。

问题3 <time.h>的简单用法

需要注意的是 tm_year 返回的是差值,且 tm_mon 是从 0 开始的

直接放代码和注释。

#include <time.h>
#include <stdio.h>
int main(){
    struct tm *t; /*因为下面用上的两个函数返回值都是指针*/
    /*time_t 其实是整数,具体是 long 还是 int 之类的可能不太一样*/
    time_t x;
    /*使用 time 函数获取基准时间到现在时间经过的秒数 这有两种方法*/
    time(&x);/*可以利用 time 改动指针 &x 对应的值*/
    x=time(NULL); /*time 返回值也是秒数,所以这样写也行*/ 
    /*NULL 也可以改成随便一个指针,但是这样一来那个指针对应的数会被修改,这需要注意*/
    t=localtime(&x);/*获取x秒对应的本地时间(UTC+8)*/
    t=gmtime(&x);/*也可以用这个函数,获取UTC标准时间*/
    /*之后便可以用上面的结构体里的东西了*/
    printf("Now is %dn",t->tm_year+1900);
    return 0;
}

 

问题4 美化

基于我对 cmd 界面的认识,我认为改动颜色可以使他更好看!

效果图

源码分享:

#include <time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define PAUSE() system("pause")
#define CLEAR() system("cls")
#define rep(i,a,b) for(int i=a;i<=b;i++)
char wday_[7][7]={"Sun. |","Mon. |","Tues.|","Wed. |","Thur.|","Fri. |","Sat.  "};
char div_line[]="============================================================";
char _16bas[]="0123456789abcdef";
void color_change(){
    /*change the color of cmd in random*/
    static char cmd_[] = "color f0";
    cmd_[7]=_16bas[rand()%7];
    system(cmd_);
}
void _statement(){
    color_change();
    printf(
        "n"
        "============================================================n"
        "Welcome to use Permanent Calendar by Qing_!n"
        "Here, you can see the monthly calendar now.n"
        "Here, you can query the calendar for anyday.n"
        "Come on, study-human! Now, enjoy your time!n"
        "Notice: I will use Chinese English to talk with you.n"
        "============================================================n"
        "n"
    );
    PAUSE(); CLEAR();
}
void put_space(int x){ while(x) x--,putchar(' '); }
void i_am_doing(){
    /*To tell user that I'm calucating.*/
    static int cc=0,p=0;
    cc=(cc+1)%25; if(cc>0) return;
    p=(p+1)%27;

    CLEAR();
    printf("n%snNow calucatingn",div_line);
    rep(i,1,p) putchar('.'); putchar('n');
    printf("%sn",div_line);
}

/*----------------------------------------------------------------------------*/

struct DATE{ int year,mon,day,wday; };
int c_day[]={0,31,0,31,30,31,30,31,31,30,31,30,31};
int bas_Y=1900;

void Fix_Mode(){
    /* May be your bas_Y is not 1900. */
    color_change(); CLEAR();
    printf(
        "%snHere is Fix-Mode.n"
        "This is an important step.nPlease input a correct year.n"
        "Before use, input the year today like this:n2020n"
        "To fix the base year of different system.n%sn",div_line,div_line
    );
    printf("The year today is:"),scanf("%d",&bas_Y);
    time_t now; time(&now);
    struct tm *t=localtime(&now);
    bas_Y-=t->tm_year;
    printf("Done. Press any key to see the change.n");
    PAUSE();
}
void input_date(struct DATE *A,int y,int m,int d,int w){
    /* Maybe i havenot use this */
    A->day=d; A->mon=m; A->year=y; A->wday=w;
}
void get_date(struct DATE *A,struct tm *t){
    /* Notice: tm_year is a delta with 1900, tm_mon is [0,11] */
    A->day=t->tm_mday; A->mon=t->tm_mon+1; 
    A->wday=t->tm_wday; A->year=t->tm_year+bas_Y;
}
int is_leap_year(int year){
    return year%100==0 ? year%400==0 : year%4==0;
}
int legal_judge(struct DATE *Q){
    if(Q->day<=0||Q->mon<=0) return 0;
    if(Q->mon>12||Q->day>31) return 0;
    if(Q->mon==2) return is_leap_year(Q->year)?Q->day<=29:Q->day<=28;
    return Q->day<=c_day[Q->mon];
}
int get_wday(int wday,int delta){
    wday+=delta;
    return wday<0?wday-wday/7*7+7:wday%7;
}
int get_day(struct DATE *Q){
    if(Q->mon==2) return is_leap_year(Q->year)?29:28;
    return c_day[Q->mon];
}
void _display(struct DATE *Q){
    /* To display the date. */
    /* The head */
    if(is_leap_year(Q->year)) printf("Do you know? %d is a leap year ~n",Q->year);
    else printf("Wuhu, i want to fly ~n");
    printf("Here: %d-%dn",Q->year,Q->mon);
    rep(i,0,6) printf("%s",wday_[i]); putchar('n');
    /* what day is it? */
    int _wday=get_wday(Q->wday,-Q->day+1),mDAY=get_day(Q);
    rep(i,0,_wday-1) put_space(2),putchar('/'),put_space(2),putchar('|');
    rep(i,1,mDAY){
        printf(i!=Q->day?" %2d  ":"[%2d] ",i);
        putchar(_wday==6?'n':'|');
        _wday=(_wday+1)%7;
    }
    if(_wday!=0){
        rep(i,_wday,5) put_space(2),putchar('/'),put_space(2),putchar('|');
        put_space(2),putchar('/');
    }
    putchar('n');
}
void calc_wday(struct DATE *Q){
    /* Base on 2020-1-1 Wed. */
    int delta=0,by=2020,bm=1,bd=1;
    /*
    while(by<Q->year){
        delta+=is_leap_year(by)?366:365; 
        by++; i_am_doing();
    }
    while(by>Q->year){
        delta-=is_leap_year(by-1)?366:365; 
        by--; i_am_doing();
    }
    */
    delta+=(Q->year-by)*365;
    delta+=((Q->year-1)/4-(by-1)/4); 
    delta-=((Q->year-1)/100-(by-1)/100);
    delta+=((Q->year-1)/400-(by-1)/400);
    by=Q->year;

    while(bm<Q->mon){
        if(bm==2) delta+=is_leap_year(by)?29:28;
        else delta+=c_day[bm];
        bm++; i_am_doing();
    }
    delta+=Q->day-bd;
    Q->wday=get_wday(3,delta);
}
void Query_Mode(){
    color_change(); CLEAR();
    printf(
        "n%snWelcome to Query-Mode!n"
        "In this mode, you can input a date like this:n"
        "1969 11 9n"
        "And I will show you the monthly calendar of the date.n"
        "Notice not to input an illegal date.n"
        "If, you do that, I may point it out.n"
        "When you want to exit this mode, input three '0':n"
        "0 0 0n"
        "Enjoy your time!n%snn",div_line,div_line
    );
    PAUSE();
    struct DATE Q; 
    while(1){
        color_change(); CLEAR();
        printf("Now tell me what date you want to query:n");
        scanf("%d%d%d",&Q.year,&Q.mon,&Q.day);
        if(Q.day==0&&Q.mon==0&&Q.year==0){
            color_change(); CLEAR();
            printf("n%snThanks for your use!n",div_line);
            printf("Now press any key to exit Query_Mode.n%snn",div_line);
            PAUSE(); return;
        }
        if(legal_judge(&Q)==0){
            printf("You input an illegal date! Try again!n");
            PAUSE();
            continue;
        }else{
            calc_wday(&Q); CLEAR();
            /* display */
            printf("%sn",div_line);
            _display(&Q);
            printf("%sn",div_line);
            /* ask for another */
            printf(
                "I have show you the calendar.n"
                "Now press any key to come back.n"
                "If you want to exit this mode, input '0 0 0' next time.n"
            );
            PAUSE();
        }
    }
    
}

/*----------------------------------------------------------------------------*/

int main(){
    srand(time(NULL));
    _statement();
    while(1){
        time_t sec_; time(&sec_);
        struct tm *p; p=localtime(&sec_);
        struct DATE now; get_date(&now,p);
        /* Display the date today. */
        color_change(); CLEAR();
        printf("Today is a good day!n");
        printf("%sn",div_line);
        _display(&now);
        printf("%sn",div_line);
        /* Ask for next option. */
        printf(
            "What do you want to do now?n"
            "Input an opt as follow to tell me.n"
            "1 - to query some date.n"
            "2 - to fix year.n"
            "3 - to exit.n"
            "If you input something else, n"
            "I will change the color for you.n"
        );
        int opt;
        printf("%snInput option:n",div_line),scanf("%d",&opt);
        if(opt==1) Query_Mode();
        if(opt==2) Fix_Mode();
        if(opt==3){
            color_change(); CLEAR();
            printf("%snSee you next time!n%sn",div_line,div_line);
            PAUSE(); break;
        }
    }
    return 0;
}

 

那么今天的文章就分享到这里了,希望对大家能够有帮助呀!

另外如果你想更好的提升你的编程能力,学好C语言C++编程!弯道超车,快人一步!

C语言C++编程学习交流圈子,QQ群1030652847点击进入】微信公众号:C语言编程学习基地

分享(源码、项目实战视频、项目笔记,基础入门教程)

欢迎转行和学习编程的伙伴,利用更多的资料学习成长比自己琢磨更快哦!

编程学习书籍分享:

编程学习视频分享:

内容来源于网络如有侵权请私信删除

文章来源: 博客园

原文链接: https://www.cnblogs.com/112Q/p/13939213.html

你还没有登录,请先登录注册
  • 还没有人评论,欢迎说说您的想法!