題目要求
編程實作萬年歷,要求:
可根據用戶輸入或系統日期進行初始化,如果用戶無輸入則顯示系統日期所在月份的月歷,并突出顯示當前日期;
可根據用戶輸入的日期查詢,并顯示查詢結果所在月份的月歷,突出顯示當前日期,并提示是否閏年
對任何不合法輸入資料,拒絕查詢并進行提示,

思路分析
可將思考、編程劃分為以下幾個模塊:
如何通過已有日期和星期推算要求的日期的星期?
如何整齊地輸出月歷?
如何獲取系統時間?
在有余力的前提下,如何美化界面?
下面對上面的幾個問題給出粗略的概述,
具體實作和技巧性地東西參考后文代碼,
問題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 %d\n",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%s\nNow calucating\n",div_line); rep(i,1,p) putchar('.'); putchar('\n'); printf("%s\n",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( "%s\nHere is Fix-Mode.\n" "This is an important step.\nPlease input a correct year.\n" "Before use, input the year today like this:\n2020\n" "To fix the base year of different system.\n%s\n",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-%d\n",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%s\nWelcome to Query-Mode!\n" "In this mode, you can input a date like this:\n" "1969 11 9\n" "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 0\n" "Enjoy your time!\n%s\n\n",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%s\nThanks for your use!\n",div_line); printf("Now press any key to exit Query_Mode.\n%s\n\n",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("%s\n",div_line); _display(&Q); printf("%s\n",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("%s\n",div_line); _display(&now); printf("%s\n",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("%s\nInput option:\n",div_line),scanf("%d",&opt); if(opt==1) Query_Mode(); if(opt==2) Fix_Mode(); if(opt==3){ color_change(); CLEAR(); printf("%s\nSee you next time!\n%s\n",div_line,div_line); PAUSE(); break; } } return 0; }
那么今天的文章就分享到這里了,希望對大家能夠有幫助呀!
另外如果你想更好的提升你的編程能力,學好C語言C++編程!彎道超車,快人一步!
C語言C++編程學習交流圈子,QQ群1030652847【點擊進入】微信公眾號:C語言編程學習基地
分享(原始碼、專案實戰視頻、專案筆記,基礎入門教程)
歡迎轉行和學習編程的伙伴,利用更多的資料學習成長比自己琢磨更快哦!
編程學習書籍分享:
編程學習視頻分享:
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/205063.html
標籤:其他
上一篇:Python爬取惠農網蘋果資料,看看新鮮的水果價格如何
下一篇:Verilog小總結