免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1131 | 回复: 0
打印 上一主题 下一主题

calendar [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-06-07 16:50 |只看该作者 |倒序浏览

       
       
       
       
       
       
万年历课程设计报告书
院校:山东科技大学泰安校区
班级:信息系计算机06-1班
姓名:张绍言
学号:0602310131
指导教师:杨在春
完成日期:2008-05-18
一.需求分析

该程序主要功能是显示用户指定日期的日历,效果像普通日历一样.如果用户并未指定日期则自动获取不前系统的日期并显示,并进入程序的命令提示符工作模式下,并允许用户使用a,d,s,w四个按键控制年份/月份的增/减,同时提供g快捷命令(go)允许用户快速跳转到指定的日期,而不到一页一页的翻.
二.概要设计
  1.类的设计
  使用Date类作为辅类,主要用于操作日期

使用Calendar作为主类,主要用于实现各个子功能.
  2.各类完成的功能
  (1)Date类

拥有两个int型变量year,month分别用于储存日期的年份和月份,提供年/月份的自增/自减函数函数,并向外提供获取年/月份当前值的接口getYear(),getMonth()以及向外提供更新当前值的接口setYear(int),setMonth(int).
  (2)Calendar类

该类作为主类,用于实现,封装程序的大部分功能.包括用于响应用户a/d的输入进行月份的减1/增1,响应用户的w/s输入进行年份的减1/增1.将输出任务分为calculateNext()和calculateBack()作别用于响应日期是大于还是小于2008/01,并在show()中进行不同的选择.将输出独立出来作为formatOutput()进行格式化输出.提供用于判断指定年是否为润年的函数isLeapYear().用于跳转到指定日期的gotoSpecifiedDate(),向外提供的间接更新内嵌子类实例值的函数setCurDateYear();
setCurDateMonth(); 以及进行无限循环并根据用户输入进行不同响应的函数loopDisplay().
  3.程序工作方式
  (1)命令行工作方式
        获取帮助信息:calendar
-help
        显示指定日期的日历:calendar
2008 09 [2009 01] ...
  (2)提示符工作方式
        Calendar>
        跳至上一月:A|a        跳至下一月:D|d
        跳至上一年:W|w        跳至下一年:S|s
        跳至指定日期:G|g
(提示:YYYY
MM,输入想要跳转到的年和月份,年4位,月2位或1位)
        获取帮助:H|h                退出:Q|        q
三.主要算法分析:
  1.确定当前月初是星期几

本程序以2008/01/01(周二)为基准,向前或向后推算当前月的一号是星期几。确认月初为星期几是为了确定将本月的一号输出在第一行的哪一个星期标识符下,以后续格式化输出做基础。
  算法如下(分两种情况):
A.当前日期大于2008/01.
首先计算当前日期与基准之间相差多少个月
int
loop=int loop=(curDate.getYear()-2008)*12 + (curDate.getMonth()-1);
然后再计算在这相差的几个月中到底相差了多少天
for(i=0,month=1;i
{
//如果到了下一年,则修正月份,重新回到1月开始计数
if(month>12)
{
month=1;
year++;
}
switch
(month)
{
//一三五七八十腊,三十一天永不差
case
1:
case
3:
case
5:
case
7:
case
8:
case
10:
case
12:
        TotalDays+=31;break;
//4,6,9,11月都只是30天
case
4:
case
6:
case
9:
case
11:
        TotalDays+=30;break;
//2月份比较特殊,润年29天,平年28天
case
2:
        if(isLeapYear(year))
                TotalDays+=29;
        else
                TotalDays+=28;
        break;
}
}
再计算当前月初是星期几
int
week=TotalDays%7+2;
B.如果日期小于2008/01
首先计算所先日期与基准相差几个月份
int
loop=(2008-curDate.getYear())*12 + (1-curDate.getMonth());
然后计算总的相差天数,并由此来推算出所先月初为星期几
int
year;
int
month;
int
i;
for(i=0,month=curDate.getMonth(),year=curDate.getYear();i
{
if(month>12)
{
month=1;
year++;
}
switch
(month)
{
case
1:
case
3:
case
5:
case
7:
case
8:
case
10:
case
12:
        TotalDays+=31;break;
case
4:
case
6:
case
9:
case
11:
        TotalDays+=30;break;
case
2:
        if(isLeapYear(year))
                TotalDays+=29;
        else
                TotalDays+=28;
        break;
}
}
再计算当前月初是星期几,并做相当的错误修正
int
week=7-(TotalDays-1)%7+1;
//ajust
the week
if(week>7)
week=week-7;
2.判断润年的方法
只能被400整除或者只能被4整除但不能被100整除
实现算法如下:
bool
leap;
leap=(year%400==0)
|| ((year%4==0)&&(year%100!=0));
return
leap;
四.面向对象的设计方法
1.日期类的设计与实现
由于程序中要用到日期类型,所以我将日期封装成一个类,并提供了外部更新和访问的接口.
声明如下:
class
Date
{
public:
        Date();
        Date(int
tyear,int tmonth);
        int
getYear();
        int
getMonth();
        void
increaseYear();
        void
decreaseYear();
        void
increaseMonth();
        void
decreaseMonth();
        void
setYear(int tyear);
        void
setMonth(int tmonth);
private:
        int
year;
        int
month;
};
实现如下:
#include
"date.h"
Date::Date()
{
year=2008;
month=5;
}
Date::Date(int
tyear,int tmonth)
{
year=tyear;
month=tmonth;
}
int
Date::getYear()
{
return
year;
}
int
Date::getMonth()
{
return
month;
}
void
Date::increaseYear()
{
year++;
}
void
Date::decreaseYear()
{
year--;
}
void
Date::increaseMonth()
{
month++;
//月份修正,当用户选择查看下一月时,month变量增1,当它增加到12后,再增1就会变成13,所以我必须修正它,月份重新从1开始,而年份增1
if(month==13)
{
year++;
month=1;
}
}
void
Date::decreaseMonth()
{
month--;
//月份修正,当用户选择查看上一月时,month变量减1,当它减少到1后,再增1就会变成0,所以我必须修正它,月份重新从12开始,而年份减1
if(month==0)
{
year--;
month=12;
}
}
void
Date::setYear(int tyear)
{
year=tyear;
}
void
Date::setMonth(int tmonth)
{
month=tmonth;
}
2.日历类的设计与实现
我在程序中用日历类作为主类,它负责处理用户的输入并加以处理,最终将结果输出.
注:该类实现代码中有一些printf语句仅做调试之用.
声明:
#include
"date.h"
class
Calendar
{
public:
        Calendar();
        Calendar(Date
date);
        //用于调整年份的函数
        void
adjustYear(bool next);
        void
adjustMonth(bool next);
        void
show();
        void
calculateNext();
        void
calculateBack();
        //用于判断是否为润年的函数,若是则返回true,否则返回false
        bool
isLeapYear(int year);
        void
gotoSpecifiedDate();
        void
loopDisplay();
        void
usage();
private:
        //内嵌的子类对象
        Date
curDate;
};
实现:
#include
"calendar.h"
#include

#include
//在默认构造函数中获取当前系统日期并作变量调整,使程序能够显示当年当月的日历
Calendar::Calendar()
{
curDate.setYear(2008);
curDate.setMonth(1);
}
Calendar::Calendar(Date
date)
{
curDate.setYear(date.getYear());
curDate.setMonth(date.getMonth());
}
//调整年份,如果用户按下W/w将传递true过来,如果按下S/s,将传递false过来
//然后调用Date类提供的公共接口,进行年份的增1减1操作
void
Calendar::adjustYear(bool next)
{
if(next==true)
{
curDate.increaseYear();
}
else
if(next==false)
{
curDate.decreaseYear();
}
}
//调整年份,如果用户按下A/a将传递true过来,如果按下D/d,将传递false过来
//然后调用Date类提供的公共接口,进行月份的增1减1操作
void
Calendar::adjustMonth(bool next)
{
if(next==true)
{
curDate.increaseMonth();
}
else
if(next==false)
{
curDate.decreaseMonth();
}
}
//统一的处理显示接口
//在内部根据当前日期的大小调用相当的函数
void
Calendar::show()
{
printf("Current
Date:\t%d-%d\n",curDate.getYear(),curDate.getMonth());
if(curDate.getYear()>=2008)
calculateNext();
else
calculateBack();
}
//如果所选日期比2008/01晚则执行此函数
void
Calendar::calculateNext()
{
printf("calculateNext
was called\n");
int
TotalDays=0;
//计算当前日期与基准之间相差多少个月份
int
loop=(curDate.getYear()-2008)*12 + (curDate.getMonth()-1);
printf("loop
month=%d\n",loop);
int
year=2008;
int
month;
int
i;
printf("curDate:%d-%d\n",curDate.getYear(),curDate.getMonth());
//统计在相差的月份中到底有多少天
for(i=0,month=1;i
{
//月份修正
if(month>12)
{
month=1;
year++;
}
printf("year=%d,month=%d,TotalDays=%d\n",year,month,TotalDays);
switch
(month)
{
case
1:
case
3:
case
5:
case
7:
case
8:
case
10:
case
12:
        TotalDays+=31;break;
case
4:
case
6:
case
9:
case
11:
        TotalDays+=30;break;
case
2:
        if(isLeapYear(year))
                TotalDays+=29;
        else
                TotalDays+=28;
        break;
}
}
printf("Total
days:%d\n",TotalDays);
//确定当前日期的月初是星期几
//用总的相差天数除以7(一周7天)得到的余数再加2(由于基准2008/01/01是从星期二开始的,而不是星期二,所以我在这是加2作调整)
int
week=TotalDays%7+2;
printf("the
first day of this month is:%d\n",week);
//格式化输出
formatOutput(curDate,week);
}
//如果所选日期比2008/01早则执行此函数
void
Calendar::calculateBack()
{
//printf("calculateBack
was called\n");
int
TotalDays=0;
//calculate
the months between current date and the base date :2008-1
int
loop=(2008-curDate.getYear())*12 + (1-curDate.getMonth());
//printf("loop
months=%d\n",loop);
//printf("Current
date: %d-%d\n",curDate.getYear(),curDate.getMonth());
//calculate
all days in these months
int
year;
int
month;
int
i;
for(i=0,month=curDate.getMonth(),year=curDate.getYear();i
{
if(month>12)
{
month=1;
year++;
}
switch
(month)
{
case
1:
case
3:
case
5:
case
7:
case
8:
case
10:
case
12:
        TotalDays+=31;break;
case
4:
case
6:
case
9:
case
11:
        TotalDays+=30;break;
case
2:
        if(isLeapYear(year))
                TotalDays+=29;
        else
                TotalDays+=28;
        break;
}
//printf("Year=%d,month=%d,totaldays=%d\n",year,month,TotalDays);
}
int
week=7-(TotalDays-1)%7+1;
//ajust
the week
if(week>7)
week=week-7;
//printf("week
=%d\n",week);
//format
output to screen
formatOutput(curDate,week);
}
//格式化输出函数的实现
Calendar
                       Current Date:2008-5
===================================================
Mon        Tue        Wed        Thu        Fri        Sat        Sun
                        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        31
===================================================
void
Calendar::formatOutput(Date date,int week)
{
int
day;
int
i;
//计算当前月有多少天,以便确定输出的天数
switch(date.getMonth())
{
case
1:
case
3:
case
5:
case
7:
case
8:
case
10:
case
12:
        day=31;break;
case
4:
case
6:
case
9:
case
11:
        day=30;break;
case
2:
        if(isLeapYear(date.getYear()))
                day=29;
        else
                day=28;
        break;
}
//printf("%d
month has %d days\n",date.getMonth(),day);
//上面装饰符
for(i=0;i
printf("=");
printf("\n");
printf("Mon\tTue\tWed\tThu\tFri\tSat\tSun\n");
//让d从1到day依次加1,输出所有的天数
int
d=1;
//第一行要预留的空格数
int
blanks=week-1;
//printf("the
first line shoud remain %d blanks\n",blanks);
for(i=0;i
printf("
  \t");
//print
the first line
//先输入第一行
for(i=blanks+1;i
{
printf("%d\t",d);
d++;
}
//just
to next line
//跳转到下面几行输出
printf("\n");
int
nextLine=1;
for(i=0;i
{
if(nextLine==8)
{
//如果一行输出多于7个数字则跳至下行输出
printf("\n");
nextLine=1;
}
printf("%d\t",d);
d++;
}
printf("\n");
//下面装饰符
for(i=0;i
printf("=");
printf("\n");
}
//在这个循环函数中接收用户的输入并调用相当的函数处理后显示结果
//A/a表示调整月份到上一个月
//D/d表示调整月份到下一个月
//W/w表示调整年份到上一年
//S/s表示调整年份到下一年
void
Calendar::loopDisplay()
{
char
choice;
while(1)
{
printf("Calendar>");
choice=getchar();
switch
(choice)
{
case
'A':
case
'a':
adjustMonth(false);show();break;
case
'D':
case
'd':
adjustMonth(true);show();break;
case
'W':
case
'w':
adjustYear(false);show();break;
case
'S':
case
's':
adjustYear(true);show();break;
case
'G':
case 'g':
gotoSpecifiedDate();show();break;
case
'H':
case
'h':
usage();break;
case
'Q':
case
'q':
exit(1);
}
}
}
//跳转到指定日期的实现
void
Calendar::gotoSpecifiedDate()
{
int
year;
int
month;
printf("YYYY
MM:");
//接收用户的输入作为年月
scanf("%d
%d",&year,&month);
//重新设置当前日期
curDate.setYear(year);
curDate.setMonth(month);
//显示
show();
}
//用于为用户提供友好的操作参考
void
Calendar::usage()
{
printf("======Calendar
Viewer Usage Reference======\n");
printf("1.Command
Line Usage\n");
printf("calendar
[options]\n");
printf("[options]:YYYY
MM\n");
printf("Example:calendar
2008 08 [2009 01] ...\n");
printf("If
no date was given out,the current date will be used.\n");
printf("2.Under
command prompt Usage\n");
printf("calendar>
[key]\n");
printf("[A/a]:Previous
Month\t[D/d]:Next Month\n");
printf("[W/w]:Previous
Year \t[S/s]:Next Year\n");
printf("[G/g]:Go
to specified date\n");
printf("[H/h]:UsageReference\t[Q/q]:Exit\n");
printf("Enjoy
yourself.E-mail:creatory@163.com\n");
}
主函数的实现描述
该程序同时支持命令行和提示符工作方式(具体使用可以参考Usage
Reference).如果用户在命令行下输入:calendar
-help则显示帮助信息;如果输入:calendar
2009 09则显示2009年9月份的日历;如果用户没有任何输入,则该程序自动获取操作系统的当前日期并将其作为默认日期显示该日期的日历.
#include
"calendar.h"
#include

#include

#include
//获取系统当前日期的头文件
#include

int
main(int argc,char **argv)
{
int
year,month;
Calendar
cur;
if(argc>=2)
{
int
i;
//printf("argc=%d\n",argc);
//判断用户输入的是否为”-help”
int
re=strcmp("-help",argv[1]);
//printf("re=%d\n",re);
if(!re)
{
//printf("equal\n");
cur.usage();
}
else
{
//依次显示用户输入日期的日历
for(int
i=1;i
{
year=atoi(argv);
month=atoi(argv[i+1]);
//printf("format
year=%d,month=%d\n",year,month);
cur.setCurDateYear(year);
cur.setCurDateMonth(month);
cur.show();
}
}
}
else
{
//获取当前日期
time_t
curTime;
struct
tm *t;
curTime=time(NULL);
t=localtime(&curTime);
year=t->tm_year+1900;
//系统从1900年开始计年的
month=t->tm_mon+1;
//由于月份默认为0-31所有这里加1
//printf("cur:year=%d,month=%d\n",year,month);
cur.setCurDateYear(year);
cur.setCurDateMonth(month);
cur.usage();
cur.show();
cur.loopDisplay();
}
return
0;
}
五.程序调试和心得
1.段错误(core
dumped)

产生段错误的原因有很多,我调试这个小程序时,产生过多个段错误,主要原因有类实例在未定义之前就使用.例如我在程序中由于定义函数的顺序杂乱,而多个函数中又用到了同一个全局类实例,所以导致了函数实现在前,类实例定义在后的现象,所以出现了段错误.另一个就是在一个for循环中产生的,for(int
i=1;i起初的时候i的终值忘了赋值为argc-1,因为在命令行参数中argc表示了命令行中所有的参数个数,其中包括当前可执行程序的名称,而argv[0]-argv[argc-1]包含了所有命令行参数,其中argv[0]为可执行程序本身的名字字符串.由于我允许用户输入多个年月参数以更方便用户的使用,但是在控制参数中一个要减去argv[0]本身,否则在从argv[]中提取字符串进行转换时可以出现引用不到的argv从来导致段错误.
2.类的public,protected,private的区别

当我在声明Date类的时候,我想到该类一定会被嵌入到其他类中用于储存日期,所以我留了一个接口
void
setYear(int tyear); void setMonth(int
tmonth);以方便其他类可以方便地更新Date类的值.起初我把它声明为protected类型,结果编译器提示出错:
date.h:
In constructor ‘Calendar::Calendar()’:
date.h:13:
错误: ‘void
Date::setYear(int)’ 是保护的
calendar.cpp:6:
错误: 在此上下文中
date.h:14:
错误: ‘void
Date::setMonth(int)’ 是保护的
calendar.cpp:7:
错误: 在此上下文中
date.h:
In constructor ‘Calendar::Calendar(Date)’:
date.h:13:
错误: ‘void
Date::setYear(int)’ 是保护的
calendar.cpp:11:
错误: 在此上下文中
date.h:14:
错误: ‘void
Date::setMonth(int)’ 是保护的
calendar.cpp:12:
错误: 在此上下文中
date.h:
In member function ‘void Calendar::gotoSpecifiedDate()’:
date.h:13:
错误: ‘void
Date::setYear(int)’ 是保护的
calendar.cpp:51:
错误: 在此上下文中
date.h:14:
错误: ‘void
Date::setMonth(int)’ 是保护的
calendar.cpp:52:
错误: 在此上下文中
make:
*** [calendar.o] 错误 1
所以我只能把它声明为public类型的访问权限.
而经过代码测试我去发现了一个比较好的方法,用于解决此类问题.我依然把那两个函数声明为protected类型,由于protected类型只能被该类的实例本身所访问,所以我在Calendar类的声明了这样两个函数:
void
setCurDateYear(int year);
void
setCurDateMonth(int month);
并在实现中调用Date类的实例去访问那两个受保护的成员函数,这样间接地更新了Date类实例的值.
void
Calendar::setCurDateYear(int year)
{
curDate.setYear(year);
}
void
Calendar::setCurDateMonth(int month)
{
curDate.setMonth(month);
}
3.调试技巧

编写程序时会出现很多意想不到的结果,比如很有可能就是因为某个变量的不确定的值,导致了整个运行结果的错误.由于我所使用的开发环境并不是IDE,没有集成那多么实用的工具也没法设置断点或监视某个变量的值,所以只好用printf语句实时的输出变量的值以备查看.这样有一处好处,可以随意的在程序中添加printf语句输入变量的值,但是这样会使程序变得杂乱,而且在程序发行前还在手工去除这此调试行,我个人比较喜欢这种调试方法.
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/56374/showart_729024.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP