C语言输出格式工整的日历——2乘6样式(详见本文)
本篇博客有更新!!!更新后效果图如下:
文章末尾的完整代码如不能在Dev-C++上完好运行,出现如下问题:
E:\Dev-Cpp\源代码\万年历.c [Error] 'for' loop initial declarations are only allowed in C99 or C11 mode
原因及解决办法:http://bbs.bccn.net/thread-436527-1-1.html
回归正题:更新内容为第三问中第2小问的代码实现(超详细注释)
最近帮朋友做一些C语言的练习题,期间遇到了个比较有意思的题目,意在考察模块化程序设计,大致要求如下:
电子万年历:
1、编写函数int isleapyear(int year);判某年是否为闰年,如该年为闰年返回1,否则返回0。编写主函数输入年份给出该年是否为闰年。
2、编写函数int dayofmonth(int year ,int month);计算某年某月有几天。主函数中输入任意的年和月,给出此年该月有几天。
3、编写万年历。请利用上述1、2已编写的函数和下边已提供的函数来完成:
(1)输入年打印出此年的日历。
(2)输入年和月打印给出此年该月的日历。
1 /*参考代码*/ 2 int firstdayofyear(int year) /*求某年的第一天是星期几*/ 3 4 { int i; 5 6 long n,days=year; 7 8 days=days*365; /* printf("\n%ld",days);*/ 9 10 for(i=1;i<year;i++)days=days+isleapyear(i); /*printf("\n%ld",days);*/ 11 12 n=days%7; 13 14 return n; 15 16 } 17 18 int firstdayofmonth(int year, int month) /* 求某年某月的第一天是星期*/ 19 20 { int i,days=0, weekdays; 21 22 weekdays=firstdayofyear(year); 23 24 for(i=1;i<month;i++)days+=dayofmonth(yeari); 25 26 return (days+weekdays)%7; 27 28 }
这道题并不算困难(给出了一些参考代码),而且根据上面给出的要求也不难得到如下思路:
第一问:根据闰年计算的规则编写一个闰年判断函数。
这里我采用的是格里高利闰年规则:
1.公元年份非4的倍数,为平年。
2.公元年份为4的倍数但非100的倍数,为闰年。
3.公元年份为100的倍数但非400的倍数,为平年。
4.公元年份为400的倍数,为闰年。
转换为C语言代码形式就成了下面这样子:
1 int isLeapyear(int year) 2 {//判断否为闰年 3 //满足上述规则2或4,返回1,否则返回0 4 return ((year%100!=0&&year%4==0) || year%400==0) ? 1 : 0; 5 }
第二问:编写函数计算某年某月有几天。
由于每一年的天数差别只在二月,而二月份天数的多少又受闰年的影响,所以我们只需要对二月进行特殊判断即可。可以利用第一问已经写过的函数,完成第二问,代码如下:
1 int dayOfMonth(int year, int month) 2 {//计算某年某月的天数 3 int x;//天数 4 switch(month){ 5 case 1:case 3:case 5:case 7:case 8:case 10:case 12: 6 x=31; 7 break; 8 case 2: 9 //闰年2月29天,平年28天 10 x=isLeapyear(year) ? 29 : 28; 11 break; 12 case 4:case 6:case 9:case 11: 13 x=30; 14 break; 15 default: break; 16 } 17 return x; 18 }
第三问:利用已知函数,根据输入打印出相应的年或月的日历。
重点在于第一小问,只要我们能正确输出某一月的日历,那么输出某年的日历就相当于依次输出1~12月的日历。参考代码中已经给出了两个非常有用的函数,分别是:
- int firstdayofyear(int year) /*求某年的第一天是星期几*/
- int firstdayofmonth(int year, int month) /* 求某年某月的第一天是星期*/
这里我的想法是先利用firstdayofmonth这个函数计算出某年某月第一天是星期几,然后计算出该月有多少天,最后利用循环输出,为了美观每七天换行一次。代码如下:
1 void displayMonth(int year, int month) 2 { 3 int start,days;//该月第一天是周几 该月的天数 4 start=firstdayofmonth(year,month); 5 days=dayOfMonth(year,month); 6 if(start==0)//如果等于零说明第一天刚好是星期天,要转换一下 7 start=7; 8 printf("一\t二\t三\t四\t五\t六\t日\n"); 9 for(int i=1;i<days+start;++i){ 10 if(i<start)//没有到该月第一天之前,输出空白 11 printf(" \t"); 12 else//从1开始依次输出日期 13 printf("%2d\t",i-start+1); 14 if(i%7==0)//满七天换行 15 printf("\n"); 16 } 17 }
第二小问就简单了,循环依次打印每月的日历就是某年的日历,代码如下:
1 void displayYear(int year) 2 {//循环调用打印月份的函数输出某年的日历 3 for(int i=1;i<=12;++i){ 4 printf("%d月:\n",i); 5 displayMonth(year,i); 6 printf("\n"); 7 } 8 }
由于初版整体打印效果比较low,这次又进行了改进。思路为每次输出一行的内容,即第i月和第i+6月(0<i<7)的日期,代码实现如下(效果图见文章开头):
1 void displayYear(int year) { 2 //左边月的天数 右边月的天数 左边月份的第一天 右边月份的第一天 3 int daysl,daysr,sl,sr; 4 //二乘六的日历表外循环共执行6次 5 for(int i=1; i<=6; ++i) { 6 //打印星期栏 7 printf(" |%2d Sun Mon Tue Wen Thu Fri Sat %2d Sun Mon Tue Wen Thu Fri Sat |\n",i,i+6); 8 //初始上述变量,m和n分别记录该月当前应输出的日期 9 int m=1, n=1; 10 sl=firstdayofmonth(year,i); 11 daysl=dayOfMonth(year,i); 12 sr=firstdayofmonth(year,i+6); 13 daysr=dayOfMonth(year,i+6); 14 //对于不是第一天不是星期天的日期要做加一变化 15 sl = sl > 0 ? sl+1 : 0; 16 sr = sr > 0 ? sr+1 : 0; 17 //该循环每次输出具体的两个月份的日历,如1/7月,2/8月…… 18 while(m < sl+daysl || n < sr+daysr) { 19 //每次循环都使用一个数组记录要输出的内容,这个大小只是为了输出美观设置的 20 //初始化值为0 21 int out[40]= {0}; 22 //-1代表要输出表示边界的"|"符号 23 out[1] = -1; 24 out[39] = -1; 25 //输出每一行的循环 ,p,q记录应输出的位置 26 for(int p=5, q=24; p<18; p+=2, q+=2, ++m, ++n) { 27 //左边的月份 28 if(m < sl+daysl) { 29 if(m<sl)//未到该月第一天,令值为-2,表示输出连续的三个空格 30 out[p]=-2; 31 else //记录正确日期的值 32 out[p]=m-sl+1; 33 } else 34 out[p]=-2;//如果该月日期输出完毕,其余的部分为了格式一致,要用空格补全 35 36 //右边的月份,原理同上 37 if(n < sr+daysr) { 38 if(n<sr) 39 out[q]=-2; 40 else 41 out[q]=n-sr+1; 42 } else 43 out[q]=-2; 44 } 45 //按照上述数组保存值所代表的意义打印出来 46 for(int t=0; t<40; ++t) { 47 if(out[t]==-1) 48 printf("|"); 49 else if(out[t]==-2) 50 printf(" "); 51 else if(out[t]==0) 52 printf(" "); 53 else 54 printf("%3d",out[t]); 55 } 56 printf("\n"); 57 } 58 } 59 }
最后把他们整合到成一个程序,代码如下:
1 #include<stdio.h> 2 3 /** 4 * 目前使用的格里高利历闰年规则如下: 5 * 1.公元年份非4的倍数,为平年。 6 * 2.公元年份为4的倍数但非100的倍数,为闰年。 7 * 3.公元年份为100的倍数但非400的倍数,为平年。 8 * 4.公元年份为400的倍数,为闰年。 9 */ 10 11 //函数声明 12 int isLeapyear(int year); //判断是否为闰年 13 int dayOfMonth(int year, int month); //计算某年某月的天数 14 int firstdayofyear(int year); //求某年的第一天是星期几 15 int firstdayofmonth(int year, int month);//求某年某月的第一天是星期几 16 void displayYear(int year); //打印某年的日历 17 void displayMonth(int year, int month); //打印某年某月的日历 18 19 int main() { 20 int year,month; 21 printf("请依次输入年份和月份:\n"); 22 scanf("%d%d",&year,&month); 23 if(year<=0 || month<1 || month>12) 24 printf("输入不合法!"); 25 else { 26 if(isLeapyear(year)) 27 printf("是闰年,该月有%d天",dayOfMonth(year, month)); 28 else 29 printf("是平年,该月有%d天",dayOfMonth(year, month)); 30 printf("\n%d年的日历如下:\n",year); 31 printf("\n +---------------------------%d年的年历--------------------------+\n",year); 32 displayYear(year); 33 printf(" +-----------------------------------------------------------------+\n"); 34 } 35 //displayMonth(year,month); 36 return 0; 37 } 38 39 int isLeapyear(int year) { 40 //判断否为闰年 41 //满足上述规则2或4,返回1,否则返回0 42 return ((year%100!=0&&year%4==0) || year%400==0) ? 1 : 0; 43 } 44 45 int dayOfMonth(int year, int month) { 46 //计算某年某月的天数 47 int x;//天数 48 switch(month) { 49 case 1: 50 case 3: 51 case 5: 52 case 7: 53 case 8: 54 case 10: 55 case 12: 56 x=31; 57 break; 58 case 2: 59 //闰年2月29天,平年28天 60 x=isLeapyear(year) ? 29 : 28; 61 break; 62 case 4: 63 case 6: 64 case 9: 65 case 11: 66 x=30; 67 break; 68 default: 69 break; 70 } 71 return x; 72 } 73 74 int firstdayofyear(int year) { 75 //求某年的第一天是星期几 76 int i; 77 long days; //天数 78 days=year*365;//从0年到year年一共有多少天 79 for(i=1; i<year; i++) //加上闰年多出的天数 80 days=days+isLeapyear(i); 81 return days%7; //取余得出year年的第一天是星期几 82 } 83 84 int firstdayofmonth(int year, int month) { 85 //求某年某月的第一天是星期几 86 int i,days=0,weekdays; //天数 星期几 87 weekdays=firstdayofyear(year); //得出year年的第一天是weekdays 88 for(i=1; i<month; i++) //计算month月之前的天数days 89 days+=dayOfMonth(year, i); 90 return (days+weekdays)%7; //取余得出year年month月的第一天是星期几 91 } 92 93 void displayYear(int year) { 94 //左边月的天数 右边月的天数 左边月份的第一天 右边月份的第一天 95 int daysl,daysr,sl,sr; 96 //二乘六的日历表外循环共执行6次 97 for(int i=1; i<=6; ++i) { 98 //打印星期栏 99 printf(" |%2d Sun Mon Tue Wen Thu Fri Sat %2d Sun Mon Tue Wen Thu Fri Sat |\n",i,i+6); 100 //初始上述变量,m和n分别记录该月当前应输出的日期 101 int m=1, n=1; 102 sl=firstdayofmonth(year,i); 103 daysl=dayOfMonth(year,i); 104 sr=firstdayofmonth(year,i+6); 105 daysr=dayOfMonth(year,i+6); 106 //对于不是第一天不是星期天的日期要做加一变化 107 sl = sl > 0 ? sl+1 : 0; 108 sr = sr > 0 ? sr+1 : 0; 109 //该循环每次输出具体的两个月份的日历,如1/7月,2/8月…… 110 while(m < sl+daysl || n < sr+daysr) { 111 //每次循环都使用一个数组记录要输出的内容,这个大小只是为了输出美观设置的 112 //初始化值为0 113 int out[40]= {0}; 114 //-1代表要输出表示边界的"|"符号 115 out[1] = -1; 116 out[39] = -1; 117 //输出每一行的循环 ,p,q记录应输出的位置 118 for(int p=5, q=24; p<18; p+=2, q+=2, ++m, ++n) { 119 //左边的月份 120 if(m < sl+daysl) { 121 if(m<sl)//未到该月第一天,令值为-2,表示输出连续的三个空格 122 out[p]=-2; 123 else //记录正确日期的值 124 out[p]=m-sl+1; 125 } else 126 out[p]=-2;//如果该月日期输出完毕,其余的部分为了格式一致,要用空格补全 127 128 //右边的月份,原理同上 129 if(n < sr+daysr) { 130 if(n<sr) 131 out[q]=-2; 132 else 133 out[q]=n-sr+1; 134 } else 135 out[q]=-2; 136 } 137 //按照上述数组保存值所代表的意义打印出来 138 for(int t=0; t<40; ++t) { 139 if(out[t]==-1) 140 printf("|"); 141 else if(out[t]==-2) 142 printf(" "); 143 else if(out[t]==0) 144 printf(" "); 145 else 146 printf("%3d",out[t]); 147 } 148 printf("\n"); 149 } 150 } 151 } 152 153 void displayMonth(int year, int month) { 154 int i,start,days;//自增变量 该月第一天是周几 该月的天数 155 start=firstdayofmonth(year,month); 156 days=dayOfMonth(year,month); 157 start = start > 0 ? start+1 : 0; 158 printf("\nSUN MON TUE WEN THU FRI SAT\n"); 159 for(i=1; i<days+start; ++i) { 160 if(i<start) { //没有到该月第一天之前,输出空白 161 printf(" "); 162 } else //从1开始依次输出日期 163 printf("%3d ",i-start+1); 164 if(i%7==0)//满七天换行 165 printf("\n"); 166 } 167 }
1 #include<stdio.h> 2 3 /** 4 * 目前使用的格里高利历闰年规则如下: 5 * 1.公元年份非4的倍数,为平年。 6 * 2.公元年份为4的倍数但非100的倍数,为闰年。 7 * 3.公元年份为100的倍数但非400的倍数,为平年。 8 * 4.公元年份为400的倍数,为闰年。 9 */ 10 11 //函数声明 12 int isLeapyear(int year); //判断是否为闰年 13 int dayOfMonth(int year, int month); //计算某年某月的天数 14 int firstdayofyear(int year); //求某年的第一天是星期几 15 int firstdayofmonth(int year, int month);//求某年某月的第一天是星期几 16 void displayYear(int year); //打印某年的日历 17 void displayMonth(int year, int month); //打印某年某月的日历 18 19 int main() 20 { 21 int year,month; 22 printf("请依次输入年份和月份:\n"); 23 scanf("%d%d",&year,&month); 24 if(year<=0 || month<1 || month>12) 25 printf("输入不合法!"); 26 else{ 27 if(isLeapyear(year)) 28 printf("是闰年,该月有%d天",dayOfMonth(year, month)); 29 else 30 printf("是平年,该月有%d天",dayOfMonth(year, month)); 31 printf("\n%d年的日历如下:\n",year); 32 displayYear(year); 33 printf("%d年%d月的日历如下:\n",year,month); 34 displayMonth(year,month); 35 } 36 return 0; 37 } 38 39 int isLeapyear(int year) 40 {//判断否为闰年 41 //满足上述规则2或4,返回1,否则返回0 42 return ((year%100!=0&&year%4==0) || year%400==0) ? 1 : 0; 43 } 44 45 int dayOfMonth(int year, int month) 46 {//计算某年某月的天数 47 int x;//天数 48 switch(month){ 49 case 1:case 3:case 5:case 7:case 8:case 10:case 12: 50 x=31; 51 break; 52 case 2: 53 //闰年2月29天,平年28天 54 x=isLeapyear(year) ? 29 : 28; 55 break; 56 case 4:case 6:case 9:case 11: 57 x=30; 58 break; 59 default: break; 60 } 61 return x; 62 } 63 64 int firstdayofyear(int year) 65 {//求某年的第一天是星期几 66 long days; //天数 67 days=year*365;//从0年到year年一共有多少天 68 for(int i=1;i<year;i++) //加上闰年多出的天数 69 days=days+isLeapyear(i); 70 return days%7; //取余得出year年的第一天是星期几 71 } 72 73 int firstdayofmonth(int year, int month) 74 {//求某年某月的第一天是星期几 75 int days=0,weekdays; //天数 星期几 76 weekdays=firstdayofyear(year); //得出year年的第一天是weekdays 77 for(int i=1;i<month;i++) //计算month月之前的天数days 78 days+=dayOfMonth(year, i); 79 return (days+weekdays)%7; //取余得出year年month月的第一天是星期几 80 } 81 82 void displayYear(int year) 83 { 84 for(int i=1;i<=12;++i){//循环调用打印月份的函数输出某年的日历 85 printf("%d月:\n",i); 86 displayMonth(year,i); 87 printf("\n"); 88 } 89 } 90 91 void displayMonth(int year, int month) 92 { 93 int start,days;//该月第一天是周几 该月的天数 94 start=firstdayofmonth(year,month); 95 days=dayOfMonth(year,month); 96 if(start==0)//如果等于零说明第一天刚好是星期天,要转换一下 97 start=7; 98 printf("一\t二\t三\t四\t五\t六\t日\n"); 99 for(int i=1;i<days+start;++i){ 100 if(i<start)//没有到该月第一天之前,输出空白 101 printf(" \t"); 102 else//从1开始依次输出日期 103 printf("%2d\t",i-start+1); 104 if(i%7==0)//满七天换行 105 printf("\n"); 106 } 107 }
运行效果见下图(旧版):
虽然说是万年历,但实际有点过于简单了(只显示公历),而且这个输入范围也有限制(不能超过int的范围),不过还是有点参考价值的。如果对你有帮助的话不妨支持一下,非常感谢🙏