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(yeari);
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的范围),不过还是有点参考价值的。如果对你有帮助的话不妨支持一下,非常感谢🙏

posted @ 2020-05-31 15:31  小柒w  阅读(1337)  评论(0编辑  收藏  举报