c语言实现万年历(大一大作业)(可查农历,节假日,周几,切换年月显示)(昨天交作业,今天上传(狗头))(萌新,如有问题,请多谅解)(使用dev c++)

本文主要是介绍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++)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/497344

相关文章

python管理工具之conda安装部署及使用详解

《python管理工具之conda安装部署及使用详解》这篇文章详细介绍了如何安装和使用conda来管理Python环境,它涵盖了从安装部署、镜像源配置到具体的conda使用方法,包括创建、激活、安装包... 目录pytpshheraerUhon管理工具:conda部署+使用一、安装部署1、 下载2、 安装3

Mysql虚拟列的使用场景

《Mysql虚拟列的使用场景》MySQL虚拟列是一种在查询时动态生成的特殊列,它不占用存储空间,可以提高查询效率和数据处理便利性,本文给大家介绍Mysql虚拟列的相关知识,感兴趣的朋友一起看看吧... 目录1. 介绍mysql虚拟列1.1 定义和作用1.2 虚拟列与普通列的区别2. MySQL虚拟列的类型2

使用MongoDB进行数据存储的操作流程

《使用MongoDB进行数据存储的操作流程》在现代应用开发中,数据存储是一个至关重要的部分,随着数据量的增大和复杂性的增加,传统的关系型数据库有时难以应对高并发和大数据量的处理需求,MongoDB作为... 目录什么是MongoDB?MongoDB的优势使用MongoDB进行数据存储1. 安装MongoDB

关于@MapperScan和@ComponentScan的使用问题

《关于@MapperScan和@ComponentScan的使用问题》文章介绍了在使用`@MapperScan`和`@ComponentScan`时可能会遇到的包扫描冲突问题,并提供了解决方法,同时,... 目录@MapperScan和@ComponentScan的使用问题报错如下原因解决办法课外拓展总结@

mysql数据库分区的使用

《mysql数据库分区的使用》MySQL分区技术通过将大表分割成多个较小片段,提高查询性能、管理效率和数据存储效率,本文就来介绍一下mysql数据库分区的使用,感兴趣的可以了解一下... 目录【一】分区的基本概念【1】物理存储与逻辑分割【2】查询性能提升【3】数据管理与维护【4】扩展性与并行处理【二】分区的

使用Python实现在Word中添加或删除超链接

《使用Python实现在Word中添加或删除超链接》在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能,本文将为大家介绍一下Python如何实现在Word中添加或... 在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能。通过添加超

MybatisGenerator文件生成不出对应文件的问题

《MybatisGenerator文件生成不出对应文件的问题》本文介绍了使用MybatisGenerator生成文件时遇到的问题及解决方法,主要步骤包括检查目标表是否存在、是否能连接到数据库、配置生成... 目录MyBATisGenerator 文件生成不出对应文件先在项目结构里引入“targetProje

Linux使用fdisk进行磁盘的相关操作

《Linux使用fdisk进行磁盘的相关操作》fdisk命令是Linux中用于管理磁盘分区的强大文本实用程序,这篇文章主要为大家详细介绍了如何使用fdisk进行磁盘的相关操作,需要的可以了解下... 目录简介基本语法示例用法列出所有分区查看指定磁盘的区分管理指定的磁盘进入交互式模式创建一个新的分区删除一个存

C#使用HttpClient进行Post请求出现超时问题的解决及优化

《C#使用HttpClient进行Post请求出现超时问题的解决及优化》最近我的控制台程序发现有时候总是出现请求超时等问题,通常好几分钟最多只有3-4个请求,在使用apipost发现并发10个5分钟也... 目录优化结论单例HttpClient连接池耗尽和并发并发异步最终优化后优化结论我直接上优化结论吧,

windos server2022里的DFS配置的实现

《windosserver2022里的DFS配置的实现》DFS是WindowsServer操作系统提供的一种功能,用于在多台服务器上集中管理共享文件夹和文件的分布式存储解决方案,本文就来介绍一下wi... 目录什么是DFS?优势:应用场景:DFS配置步骤什么是DFS?DFS指的是分布式文件系统(Distr