编程小技巧

    返回首页    发表留言
本文作者:李德强
          技巧二十六:显示精美日历
 
 

        今天跟大家分享的小技巧是关于显示日历的。我们常常使用日历程序,电脑上的日历、手机上的日历、台历等等,这些日历的日期是如何计算出来,如何按一定的格式来显示的呢?今天我们就来跟大家一起分享精美日历的显示程序。有的朋友可能觉得显示一个指定月份的日历并不复杂,但请不要小看这个程序,这一个看似简单的程序里有很多需要主意和处理的细节问题,如果处理的不好,将直接导致错误的结果。所以请跟我们一起动手实现这个日历程序,我们先来看看要实现的程序的基本功能:

1.接收用户输入的年份和月份;

2.如果是二月,则需要做闰年判断;

3.计算当前月份第一天的星期;

4.并按星期的方式显示日历。

 

一、接收用户输入

        我们首先需要处理用户输入问题,我们知道main函数是具有2个参数的,一个是int argc用于存放用户输入命令的参数个数,另一个是char *argv[]用于存放用户输入的参数表,这个变量其实是一个字符型指针数组(其实可以解理为多个字符串)。所以当我们接收到用户输入的参数之后就可以将这两个参数转为年份year和月份month了,我们来看一下代码实现:

 

int main(int argc, char* argv[])
{
	// do something
	return 0;
}

 

        当用户在命令行中输入:

./cal 2017 10

        回车后,我们的cal程序就可以通过int argc和char* argv[]来取得用户的输入,argc的值为3,表示有3个参数,argv数组的长度为3表示有3个字符串用于存放这些参数内容,分别为:

argv[0]内容为"cal"

argv[1]内容为"2017"

argv[2]内容为"10"

        于是我们就通过argv这个变量取得了用户的输入内容。不过需要注意的是,2017和10都是字符串,我们需要将它们转为整型变量才方便计算,所以我们用sscanf函数来将它们由字符串转为整数:

 

int year;
int month;
//接收主函数参数,参数1为年份,参数2为月份(1~12)
sscanf(argv[1], "%d", &year);
sscanf(argv[2], "%d", &month);

 

二、计算闰年

        当得到了用户想要显示的年和月之后,我们还需要根据公式来计算闰年,因为二月共有28天,如果是闰年二月则有29天。闰年的计算公式如下:

能被4整除但不能被100整除的年份和能被400整除的年份为闰年

        所以我们必须写一个用于计算闰年功能,这个功能如下:

 

if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
{
        //闰年,二月有29天
}

    

        这个功能很简单就是将上面用文字来描述闰年的方法转为了程序代码,比较容易理解。

 

三、计算星期

        计算星期是一个重要的功能,因为我们希望展现给用户的是一个带有星期功能的日历,可以让用户清楚的看到当前月份的每一天是星期几,所以我们就必须要计算星期。计算星期有很多种方法,这里我们给出一个比较通用的计算公式——蔡勒(Zeller)公式:

星期 = 年 + [年 / 4] + [世纪 / 4] - 2 × 世纪 + [26 × (月 + 1) / 10] + 天 - 1

        这里有几个需要注意的地方:

1.用蔡勒公式计算的星期只适合于1582年10月15日之后的日期;

2.如果是一月和二月则按上一年的十三月、十四月处理;

3.年份只使用个位和十位计算;

4.得到的结果除以7取余数;

5.结果:0表示星期天,1表示星期一,... ,6表示星期六

        所以我们得到最终用于计算星期的蔡勒公式函数为:

 

int week_from_zeller(int year, int month, int day)
{
	if (month <= 2)
	{
		month += 12;
		year--;
	}
	int century = year / 100;
	year %= 100;
	int days = (year + year / 4 + century / 4 - 2 * century + 26 * (month + 1) / 10 + day - 1) % 7;
	while (days < 0)
	{
		days += 7;
	}
	return days;
}

 

四、显示日历

        最后,我们来编写显示日历的程序,我们需要定义3个不同的数组,分别用于存放月份的名称、星期的简称、每个月的天数:

char* month_name[12] = { "January", "February", "March", ... , "December" };
char* week_name[7] = { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" };
int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

 

        这里还有一个细节需要注意,我们在这里使用month变量做为月份名称数组的下标,所以我们需要将用户输入的1~12修改为0~11(如果有朋友不理解为什么要这样处理请参见《数组的下标》)。。其它功能则为正常显示排版:

//输入的月份为1~12,转为0~11
month--;
//显示年、月和星期
printf("     %s %d\n", month_name[month], year);
for (int j = 0; j < 7; j++)
{
	printf("%2s ", week_name[j]);
}
printf("\n");
//显示每月1日前的空白
for (int i = 0; i < week % 7; i++)
{
	printf("   ");
}
//循环显示日期
for (int i = 1; i <= days[month]; i++)
{
	printf("%2d ", i);
	//显示7个数后换行
	if ((i + week) % 7 == 0)
	{
		printf("\n");
	}
}

 

        最后,我们来看一下显示的程序结果,在控制台中分别输入

./cal 1998 2

./cal 2000 2

./cal 2016 2

./cal 2017 11

        然后看一看程序的运行结果:

 

     February 1998
Su Mo Tu We Th Fr Sa 
 1  2  3  4  5  6  7 
 8  9 10 11 12 13 14 
15 16 17 18 19 20 21 
22 23 24 25 26 27 28 


     February 2000
Su Mo Tu We Th Fr Sa 
       1  2  3  4  5 
 6  7  8  9 10 11 12 
13 14 15 16 17 18 19 
20 21 22 23 24 25 26 
27 28 29 

     February 2016
Su Mo Tu We Th Fr Sa 
    1  2  3  4  5  6 
 7  8  9 10 11 12 13 
14 15 16 17 18 19 20 
21 22 23 24 25 26 27 
28 29 

     November 2017
Su Mo Tu We Th Fr Sa 
          1  2  3  4 
 5  6  7  8  9 10 11 
12 13 14 15 16 17 18 
19 20 21 22 23 24 25 
26 27 28 29 30 

 

        我们显示了4个月份的日历,分别是1998年二月、2000年二月(闰年)、2016年二月(闰年)、2017年十一月。

        这里还有一些小问题:程序中并没有处理用户输入非法的情况。例如用户输入参数格式不对./cal abc def或缺少参数./cal时,程序会出现错误不能显示正常的结果。请大家自己思考并手动解决用户输入非法的相关问题。

本例代码下载(请将下载后的文件修改为example.tar.gz解压即可)

 

 

        今天的小技巧你学会了吗?

 

 

 

 

    返回首页    返回顶部
  看不清?点击刷新

 

  Copyright © 2015-2023 问渠网 辽ICP备15013245号