本文主要是介绍c语言实现万年历(大一大作业)(可查农历,节假日,周几,切换年月显示)(昨天交作业,今天上传(狗头))(萌新,如有问题,请多谅解)(使用dev c++),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
这篇文章本意只是为了大家的万年历的实现方式提供一种思路,代码本身并不成熟,需要源代码的同学,本人已经上传到网站上了,设置的免费下载,csdn本身设置的免费下载需要做任务,但是绝对是可以下载的,请不要私信问作者资源需要付费下载的问题。并且文件在dev-c++环境下是可以运行的。如果你不会解压当我没说。字体重叠是因为没有全屏,或者屏幕缩放率不为100%造成的,请自行百度修改。
重新上传了资源,正在审核,为了方便大家 ,评论区也添加了百度云盘链接
一、先看效果图
1.年度显示
2.月度显示
3.查询日
二、思路及代码实现
做这个程序我个人的思路是“查询日→→→查询月→→→查询年”这样的流程。因为我作业老师要求英文注释,我发到CSDN里面改成了中文,所以代码里面可能会有没删干净的英文注释。
1.查询周几
查询日需要做的就是可以计算出需要查询的日期是周几,以及农历日期,和节假日。我个人认为农历日期的查询是最难做的,因为它的算法乱七八糟的,我这里用了一种比较取巧的方式。
先说查询周几吧,这个应该都会,我就比较粗糙说一下。
a.确定基准。
我们以2000年1月1日作为基准,这一天是周六,冬月廿五,元旦。如果你想查之前的日子,那么你就把基准往前定。
b.计算要查的日期与基准所差的天数
这个部分的核心比较简单,只需要会判断中间每个年份是不是闰年,最后一年每个月都有几天,然后加起来就可以了。
//判断闰年
int leapyear (int y){int i=0;if(y%100==0){ //年份在可以被100整除的情况下必须被400整除才算闰年if(y%400==0){i=1;}} else {if (y%4==0){i=1;}}return i; //如果是闰年就返回1 平年就返回0}
int week (int y,int m,int d){ //y年 m月 d天int i,j,w,sum1,sum2; //w 周几int a[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};sum1=0;sum2=0;w=6; //基准那天是周六 if(leapyear(y)==1){a[2]=29; //如果是闰年那么2月就有29天} for(i=1;i<m;i++){sum2+=a[i];} sum2+=d; //这时候是最后一年过了多少天sum1+=sum2;for(j=2000;j<y;j++){if(leapyear(j)==1){sum2=366;} else {sum2=365;}sum1+=sum2; //加上中间每年的天数} sum1+=w;sum1-=1; //2000年1月1日已经过去了w=sum1%7;return w; //把周几返回去
}
c.需要打印周几的时候使用的函数
void printweek(int w){ //这个w是上个函数返回的wswitch(w) {case 0:printf("周日");break;case 1:printf("周一");break;case 2:printf("周二");break;case 3:printf("周三");break;case 4:printf("周四");break;case 5:printf("周五");break;case 6:printf("周六");break;}}
2.查询农历
农历的数据都是南京紫金山天文台历算室算出来的,有的节气是精确到时辰的,我弄了好长时间都不太会,所以我网上找了一个农历数据表。把他变成了txt文本,只保留了需要的农历日期。
这是原来的文本
我改过后的
因为我们老师只要求常见的节假日,那些节气不做要求,我都去掉了,只留下了农历日期,节假日单独出一个函数。
然后农历闰x月我是直接在月前面加了一个3
比如闰5月就会变成35,闰冬月会变成311
把农历也变成数字是因为txt读取中文的时候可能会出错,我的作业肯定要求稳(狗头
此外因为我的农历是取决于我这个txt的范围的,如果你想扩大查询范围,你要先找到足够大的农历数据。
这个读取主要是使用
ch=fgetc(p);t=atoi(a);switch(x);这三个代码实现的。
//注释看的时候要和修改后的txt文档结合看
void lunarcalendar(int y,int m,int d,int *p1,int *p2) //y 年 m 月 d 天 p1指向lm即农历月 p2指向农历天
{int i,t,z,q;char a[10]={0}; //这个是用来存储和年份有关的字符的
字符定义范围搞大点没什么影响char b[10]={0}; //这个是用来存储和月份有关的字符的 char c[40]={0}; //这个是用来存储和天份有关的字符的i=0;t=0; //atoi转化出来的数字年份q=0; //atoi转化出来的数字月份z=0; //atoi转化出来的数字天FILE*p;char ch;if((p=fopen("calendar.txt","r"))==NULL) //打开txt文本{printf("ERROR\n");}for(; ;){ //这里开始一个循环for(;ch!='a';){ //持续不断地读入字符,读到“a”的时候进行下一步ch=fgetc(p); }for(i=0;i<10;i++){a[i]=0; //每次都想先把储存年份有关的字符都归零,这步很关键}for(i=0;;){ ch=fgetc(p);if(ch!='/'){ //将字符依次赋值给字符数组,直到读到“/”的时候停止赋值a[i]=ch;i++; }else {break;} }t=atoi(a); //把字符年份变成数字年份并赋值 for(i=0;i<10;i++){ //月份初始化,后面和年份操作相同b[i]=0;}for(i=0; ;){ ch=fgetc(p);if(ch!='/'){ b[i]=ch;i++;}else {break;} }q=atoi(b); //换成数字月份for(i=0;i<40;i++){c[i]=0; }for(i=0;;){ ch=fgetc(p);if(ch!=','){ c[i]=ch;i++;}else {break;} }z=atoi(c); //换成数字天数if(t==y){if(q==m){if(z==d){break; //当年月日都和你想查的对应上之后,再进行下一部,否则会持续不断进行下去,如果你查了txt中不存在的月份,那么就会出bug,程序不会显示结果}}} } for(i=0;i<10;i++){b[i]=0; //初始化数组,现在这个数组用来储存农历月份}for(i=0;i<40;i++){c[i]=0; //初始化数组,现在这个数组用来储存农历日期} for(i=0;;){ ch=fgetc(p);if(ch!=','){ //读入农历月份b[i]=ch;i++;} else{break;}}q=atoi(b); //转化为数字农历月份for(i=0;;){ch=fgetc(p);if(ch!=','){ //读入农历天数 c[i]=ch;i++;}else{break;}}z=atoi(c); // 转化为数字农历天数fclose(p); //关闭txt文件*p1=q;*p2=z; //用指针赋值,这样就可以把结果弄出函数
}
可能有人看见代码,会问:“为什么每次读入字符前初始化字符数组很重要?”
for(i=0;i<10;i++){
a[i]=0;
}因为每次程序都在读入年月日,不匹配的时候重新进行循环(循环的时候读入并不会初始化),所以这个循环势必要循环很多次,因此不进行初始化的时候 a[1],a[2],a[3],a[4]其实都是有值的,读入年份的时候不受影响,因为年都是四位数,但是读入月份的时候,如果不进行初始化,可能当你读到1月,并进行赋值,通过atoi转换到数字的时候会变成12月,这是因为a[2]的赋值在上一个循环读入12月的时候被赋值了12中的2,这次只读入了1个数字但是a[2]不是0,atoi读到的还是12,转化出来的数字就还是12.
void lunarcalendarmonth(int lm)
//lm 是p1指向的变量,农历月份
{switch(lm){case 1:printf("正月");break;case 2:printf("二月");break;case 3:printf("三月");break;case 4:printf("四月");break;case 5:printf("五月");break;case 6:printf("六月");break;case 7:printf("七月");break;case 8:printf("八月");break;case 9:printf("九月");break;case 10:printf("十月");break;case 11:printf("冬月");break;case 12:printf("腊月");break;case 31:printf("闰正月");break;case 32:printf("闰二月");break;case 33:printf("闰三月");break;case 34:printf("闰四月");break;case 35:printf("闰五月");break;case 36:printf("闰六月");break;case 37:printf("闰七月");break;case 38:printf("闰八月");break;case 39:printf("闰九月");break;case 310:printf("闰十月");break;case 311:printf("闰冬月");break;case 312:printf("闰腊月");break; }
}
void lunarcalendarday(int ld)
//ld是上上个函数p2所指向的变量,是农历天数
{switch(ld){case 1:printf("初一");break; case 2:printf("初二");break; case 3:printf("初三");break; case 4:printf("初四");break; case 5:printf("初五");break; case 6:printf("初六");break; case 7:printf("初七");break; case 8:printf("初八");break; case 9:printf("初九");break; case 10:printf("初十");break; case 11:printf("十一");break; case 12:printf("十二");break; case 13:printf("十三");break; case 14:printf("十四");break; case 15:printf("十五");break; case 16:printf("十六");break; case 17:printf("十七");break; case 18:printf("十八");break; case 19:printf("十九");break; case 20:printf("二十");break; case 21:printf("廿一");break; case 22:printf("廿二");break; case 23:printf("廿三");break; case 24:printf("廿四");break; case 25:printf("廿五");break;case 26:printf("廿六");break; case 27:printf("廿七");break;case 28:printf("廿八");break;case 29:printf("廿九");break; case 30:printf("三十");break; }
}
3.查询节假日
做到这里的时候,我们已经可以查出一天的周几,以及农历信息了,然后可以做出一个查询节假日的功能。给出的代码写法只可以查固定日期的节假日,如果想查二十四节气这种的则需要上一步的txt文本进行修改,添加数据,并且对读取代码做一些修改。
int festival(int y,int m,int d,int lm,int ld,int mod) //mod 是两个模式,输入1是这一天如果有节假日就只输出一个。主要用来查询整个月的时候 。输入2 是如果这一天有多个节假日,所有的都会输出。主要用于查询天
{int count=0; //count记录这一天有几个节假日color(3); //一般的节日输出颜色变为蓝色if(m==1){if(d==1){printf("元旦");count++; }}if(m==2){if(d==14){if(count!=0){if(mod==1){goto end; //mod 1 情况下遇到第二个节日直接结束这个函数,}printf("|"); //mod2下有多个节日用“|”分割}printf("情人节");count++; //Add one to the number of festivals}}end:color(7); //节日输出之后的输出字符颜色变为白色return count; //把今天有几个节日传回去
}
这里的color函数是自己定义的一个函数,可以改变输出字符的颜色。
0 = 黑色 8 = 灰色
1 = 蓝色 9 = 淡蓝色
2 = 绿色 A = 淡绿色
3 = 浅绿色 B = 淡浅绿色
4 = 红色 C = 淡红色
5 = 紫色 D = 淡紫色
6 = 黄色 E = 淡黄色
7 = 白色 F = 亮白色
#include <windows.h> //定义的时候加上这行void color(int x) if(x>=0&&x<=15){SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), x);}else{color(7);
}
4.按月输出
按照月输出的时候,我们已经解决了最复杂的农历,现在只需要设置输出的位置就可以解决这个问题。
按月输出的核心难题是光标的移动。这里使用和color()函数相同的定义方法。
我们只需要会用这个函数就行了,万年历并不需要我们有深入的理解。(其实我也不会,我只是一个废物大学生)
#include <windows.h>
int wherex()
{CONSOLE_SCREEN_BUFFER_INFO pBuffer;GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &pBuffer);return (pBuffer.dwCursorPosition.X+1);
}
//得到光标的x坐标
int wherey()
{CONSOLE_SCREEN_BUFFER_INFO pBuffer;GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &pBuffer);return (pBuffer.dwCursorPosition.Y+1);
}
//得到光标的y坐标void gotoxy(int x,int y)
{COORD c;c.X=x-1;c.Y=y-1;SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),c);
}
//移动光标到指定位置
void month (int y ,int m){int d,ld,lm,*p1,*p2;d=1; ld=25;lm=11;p1=&lm;p2=&ld;//上面几个数的定义和之前的函数一样,注意不要把y当成坐标yint i,j,w,z,x1,y1,x2,y2,y3,t;t=0;d=1; //输出月从1号开始(废话)int a[13]={0,31,28,31,30,31,30,31,31,30,31,30,31}; if(leapyear(y)==1){a[2]=29;} //把这年的二月变成正确的天数,输出年的时候有用。w=week(y,m,d); //利用函数求出这天是周几.z=wherex(); //得到现在光标的x坐标z+=5; //记录的数据加5,可以根据自己的电脑进行修改gotoxy(z,wherey()); //将光标向右移动5,输出月的时候可以与左边流出空隙,这里可以忽略 printf("-----------------------------------------------");//分割线gotoxy(z,wherey()+1); //换行的时候不要简单使用“\n”,输出月的时候没有影响,但是会对年产生影响printf(" %d年%d月",y,m); //月份头gotoxy(z,wherey()+1); //利用函数进行换行printf("-----------------------------------------------");//分割线gotoxy(z,wherey()+1); //换行printf("周日 周一 周二 周三 周四 周五 周六 ");gotoxy(z,wherey()+2); //一次换两行,留出空隙,使输出结果美观一些for(i=0,j=0;i<w;i++,j++){printf(" ");} //利用空格将第一天与周几对齐for(d=1;d<=a[m];d++,j++){lunarcalendar(y,m,d,p1,p2); //先查一下这个农历信息x1=wherex(); y1=wherey(); //记录现在输出数字时光标的位置if(d<10){printf(" %d ",d);}else{printf(" %d ",d);} //为了美观留有空格,并输出x号x2=wherex();y2=wherey(); //记录第二个坐标gotoxy(x1,y1+1); //往数字的下一行相同位置进行移动光标t=festival(y,m,d,lm,ld,1);//输出有颜色节日if(t==0){color(8);if(ld==1){lunarcalendarmonth(lm);}else{lunarcalendarday(ld);}//如果没有节日就输出灰色的农历日,但如果则这一天是初一,就输出月份,这样才能知道农历是几月(比如正月初一就输出正月 正月初二就输出初二)(老师要求的真细)}color(7);//之后的输出内容变成白色gotoxy(x2,y2); //把坐标移到下一个数字应该开始的地方 if(j==6){if(d<a[m]){gotoxy(z,wherey()+3);j=-1;}}}
//如果这一行已经满了,并且这一个月还没有结束,那就换行。gotoxy(z,wherey()+2); //这个月日期都结束后,光标移到下一个分割线位置printf("-----------------------------------------------");//分割线gotoxy(z,wherey()+1); //换行
}
以下就是输出一个月的日历的主代码
printf("请根据'200001'输入年月\n");
scanf("%4d%2d",&y,&m);
month(y,m); //打出这一个月的日历
gotoxy(1,wherey());//让光标靠左
4.按年输出
当我们实现按月输出后,按年输出的代码就变得简单了起来,只需要一个简单的for循环加移动光标等就可以了。
按月输出的时候之所以不直接用换行符是因为按年输出的时候,一旦换行,这个月的输出结果就会跑到上个月数据的地方,有兴趣的可以试试直接用换行符会发生什么(狗头)。
void year(int y)
{int x1,y1,x2,y2,max;max=0; //纪录每个月分割线最靠下的y坐标int m=1;int count=0;int a[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};if(leapyear(y)==1){a[2]=29;}for(m=1;m<13;m++){x1=wherex();y1=wherey(); month(y,m);if(max<wherey()){max=wherey(); } //纪录每个月分割线最靠下的y坐标count++;if(count%4==0){x2=wherex();y2=wherey();gotoxy(1,max+3); //为了排版,max是必要的,要不然容易挤掉其他月份的输出结果}else{gotoxy(x1+58,y1); //将光标移到上个月开始的右边58字符的位置}}
//每四个月换一次行变成4*3的排列方式,自己也可以改成3*4或其他的}
三、总结
万年历想要做细,想要考虑的细节还真不少。这是我的大一作业,也是第一个真正意义上的程序,所以肯定有很多没必要的代码,很多不够简便的方法,希望各位大佬看到之后多多谅解。
这篇关于c语言实现万年历(大一大作业)(可查农历,节假日,周几,切换年月显示)(昨天交作业,今天上传(狗头))(萌新,如有问题,请多谅解)(使用dev c++)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!