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

相关文章

linux生产者,消费者问题

pthread_cond_wait() :用于阻塞当前线程,等待别的线程使用pthread_cond_signal()或pthread_cond_broadcast来唤醒它。 pthread_cond_wait() 必须与pthread_mutex 配套使用。pthread_cond_wait()函数一进入wait状态就会自动release mutex。当其他线程通过pthread

C语言中联合体union的使用

本文编辑整理自: http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=179471 一、前言 “联合体”(union)与“结构体”(struct)有一些相似之处。但两者有本质上的不同。在结构体中,各成员有各自的内存空间, 一个结构变量的总长度是各成员长度之和。而在“联合”中,各成员共享一段内存空间, 一个联合变量

关于C++中的虚拟继承的一些总结(虚拟继承,覆盖,派生,隐藏)

1.为什么要引入虚拟继承 虚拟继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。如:类D继承自类B1、B2,而类B1、B2都继承自类A,因此在类D中两次出现类A中的变量和函数。为了节省内存空间,可以将B1、B2对A的继承定义为虚拟继承,而A就成了虚拟基类。实现的代码如下: class A class B1:public virtual A; class B2:pu

C++对象布局及多态实现探索之内存布局(整理的很多链接)

本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等 文章链接:http://dev.yesky.com/254/2191254.shtml      论C/C++函数间动态内存的传递 (2005-07-30)   当你涉及到C/C++的核心编程的时候,你会无止境地与内存管理打交道。 文章链接:http://dev.yesky

C++的模板(八):子系统

平常所见的大部分模板代码,模板所传的参数类型,到了模板里面,或实例化为对象,或嵌入模板内部结构中,或在模板内又派生了子类。不管怎样,最终他们在模板内,直接或间接,都实例化成对象了。 但这不是唯一的用法。试想一下。如果在模板内限制调用参数类型的构造函数会发生什么?参数类的对象在模板内无法构造。他们只能从模板的成员函数传入。模板不保存这些对象或者只保存他们的指针。因为构造函数被分离,这些指针在模板外

问题:第一次世界大战的起止时间是 #其他#学习方法#微信

问题:第一次世界大战的起止时间是 A.1913 ~1918 年 B.1913 ~1918 年 C.1914 ~1918 年 D.1914 ~1919 年 参考答案如图所示

Tolua使用笔记(上)

目录   1.准备工作 2.运行例子 01.HelloWorld:在C#中,创建和销毁Lua虚拟机 和 简单调用。 02.ScriptsFromFile:在C#中,对一个lua文件的执行调用 03.CallLuaFunction:在C#中,对lua函数的操作 04.AccessingLuaVariables:在C#中,对lua变量的操作 05.LuaCoroutine:在Lua中,

C++工程编译链接错误汇总VisualStudio

目录 一些小的知识点 make工具 可以使用windows下的事件查看器崩溃的地方 dumpbin工具查看dll是32位还是64位的 _MSC_VER .cc 和.cpp 【VC++目录中的包含目录】 vs 【C/C++常规中的附加包含目录】——头文件所在目录如何怎么添加,添加了以后搜索头文件就会到这些个路径下搜索了 include<> 和 include"" WinMain 和

Vim使用基础篇

本文内容大部分来自 vimtutor,自带的教程的总结。在终端输入vimtutor 即可进入教程。 先总结一下,然后再分别介绍正常模式,插入模式,和可视模式三种模式下的命令。 目录 看完以后的汇总 1.正常模式(Normal模式) 1.移动光标 2.删除 3.【:】输入符 4.撤销 5.替换 6.重复命令【. ; ,】 7.复制粘贴 8.缩进 2.插入模式 INSERT

C/C++的编译和链接过程

目录 从源文件生成可执行文件(书中第2章) 1.Preprocessing预处理——预处理器cpp 2.Compilation编译——编译器cll ps:vs中优化选项设置 3.Assembly汇编——汇编器as ps:vs中汇编输出文件设置 4.Linking链接——链接器ld 符号 模块,库 链接过程——链接器 链接过程 1.简单链接的例子 2.链接过程 3.地址和