2013年7月17日 星期三

[C]萬年曆實作

請設計一個萬年曆程式。程式需支援以下兩個功能:

(1) 輸入一個數字 Y表示年份、M表示月份,接著印出這個月份的日曆。
(2) 輸入一個數字 Y表示年份,接著印出該年各月的日曆。
(3)可選擇單欄印出 或者 雙欄印出的選項

每一個月的日曆中的星期次序為「日、一、二、三、四、五、六」,此外,請上網 Google 
查詢如何計算任一日屬於一星期中的哪一日。(關鍵字:「Zeller’s formula」或「蔡勒公式」)





Solution Code


#include <stdio.h>
#include <stdlib.h>

void main() {
	int choice;
	while(true) {
		choice = 0;	//每次把選擇都初始化
		printf("請選擇\n (1)進入程式\n (2)離開程式\n> ");
		fflush(stdin);
		scanf_s("%d", &choice);
		printf("\n");

		//離開程式
		if(choice == 2) {
			printf("See you!\n");
			break;
		}
		//進入程式
		else if(choice == 1) {

			//引導使用者輸入
			int mode = 0, column = 0, yy = 0, mm = 0;
			do {
				printf("請選擇印出模式\n (1)單欄\n (2)雙欄\n> ");
				fflush(stdin);
				scanf_s("%d", &column);
				if(column != 1 && column != 2)
					printf("\n僅支援單欄或雙欄印出!! 請重新輸入\n\n");
			}while(column != 1 && column != 2);

			do {
				printf("\n請選擇指定方法\n (1)指定年份\n (2)指定年份與月份\n> ");
				fflush(stdin);
				scanf_s("%d", &mode);
				if(mode != 1 && mode != 2)
					printf("\n不存在的指定方法!! 請重新輸入\n");
			}while(mode != 1 && mode != 2);

			do {
				printf("\n請輸入年份\n> ");
				fflush(stdin);
				scanf_s("%d", &yy);
				if(yy <= 0)
					printf("\n不支援西元前!! 請重新輸入\n");
			}while(yy <= 0);

			if(mode == 2) {
				do {
					printf("\n請輸入月份\n> ");
					scanf_s("%d", &mm);
					if(mm < 1 || mm > 12)
						printf("\n錯誤的月份!! 請重新輸入\n");
				}while(mm < 1 || mm > 12);
			}

			//開始印出萬年曆,先偷偷測閏年

			/*
			偷偷測閏年
			閏年條件:
				1.西元年被400整除
				2.西元年被4整除但不被100整除
			*/
			int leapYear = 0;
			if(yy % 400 == 0 || (yy % 4 == 0 && yy % 100 != 0))
				leapYear = 1;

			//判斷模式,印某年全部或者特定月份
			if (mode == 2) {	
				//只印特定月份的狀況
				int day = 1, weekOfStart, dayOfMonth, week;
				printf("\n    %d 年 %d 月\n", yy, mm);
				printf("日 一 二 三 四 五 六\n");

				//先來算算這個月有幾天...
				switch(mm) {
					case 1:
					case 3:
					case 5:
					case 7:
					case 8:
					case 10:
					case 12:
						dayOfMonth = 31;
						break;
					case 2:
						if(leapYear)
							dayOfMonth = 29;
						else
							dayOfMonth = 28;
						break;
					case 4:
					case 6:
					case 9:
					case 11:
						dayOfMonth = 30;
						break;
				}
				/*
				開始印日期囉~
				看成表格的方式來印
				*/

				//先來找第一天是星期幾
				if(mm == 1 || mm == 2) // 1月和2月要特別處理
					weekOfStart = ((yy-1)%100 + ((yy-1)%100)/4 + ((yy-1))/100/4 - 2*((yy-1)/100) + 26*(mm+13)/10);
				else
					weekOfStart = (yy%100 + (yy%100)/4 + (yy/100)/4 - 2*(yy/100) + 26*(mm+1)/10);

				if(weekOfStart < 0)
					weekOfStart = (weekOfStart%7+7)%7;
				else
					weekOfStart = weekOfStart%7;

				//印出萬年曆
				while(day <= dayOfMonth) {
					for(week = 0;week < 7;week++) {
						if(day == 1)
						{
							if(week == weekOfStart) {
								printf("01 ");
								day++;
							}
							else
								printf("   ");
						} 
						else if(day <= dayOfMonth) {
							if(day < 10)
								printf("0");
							printf("%d",day);
							if(week < 6)
								printf(" ");
							day++;
						}
					}
					printf("\n");
				}
				printf("\n");
			} else {
				//印某年全部
				if(column == 1) {
					for(mm = 1;mm < 13;mm++) {	//控制月份
						int day = 1, weekOfStart, dayOfMonth, week;
						printf("\n    %d 年 %d 月\n", yy, mm);
						printf("日 一 二 三 四 五 六\n");

						//先來算算這個月有幾天...
						switch(mm) {
							case 1:
							case 3:
							case 5:
							case 7:
							case 8:
							case 10:
							case 12:
								dayOfMonth = 31;
								break;
							case 2:
								if(leapYear)
									dayOfMonth = 29;
								else
									dayOfMonth = 28;
								break;
							case 4:
							case 6:
							case 9:
							case 11:
								dayOfMonth = 30;
								break;
						}
						/*
						開始印日期囉~
						看成表格的方式來印
						*/

						//先來找第一天是星期幾
						if(mm == 1 || mm == 2)	// 1月和2月要特別處理
							weekOfStart = ((yy-1)%100 + ((yy-1)%100)/4 + ((yy-1)/100)/4 - 2*((yy-1)/100) + 26*(mm+13)/10);
						else
							weekOfStart = (yy%100 + (yy%100)/4 + (yy/100)/4 - 2*(yy/100) + 26*(mm+1)/10);

						if(weekOfStart < 0)
							weekOfStart = (weekOfStart%7+7)%7;
						else
							weekOfStart = weekOfStart%7;

						//印萬年曆了
						while(day <= dayOfMonth) {
							for(week = 0;week < 7;week++) {
								if(day == 1) {
									if(week == weekOfStart) {	//直到和該月第一天星期一樣才開始印日期
										printf("01 ");
										day++;
									}
									else
										printf("   ");
								} 
								else if(day <= dayOfMonth)  {
									if(day < 10)
										printf("0");
									printf("%d",day);
									if(week < 6)
										printf(" ");
									day++;
								}
							}
							if(day <= dayOfMonth)
								printf("\n");	//週換行
						}
						printf("\n");	//月份換行
					}
					printf("\n");	//最後空一行
				} else {
					//用兩欄去印
					for(mm = 1;mm < 13;mm+=2) {	//控制月份,一次跳兩個月
						int dayOdd = 1, dayEven = 1, weekOfStartOfOdd, weekOfStartOfEven, dayOfMonthOfOdd, dayOfMonthOfEven, week;
						printf("\n    %d 年 %d 月\t\t    %d 年 %d 月\n", yy, mm, yy, mm+1);
						printf("日 一 二 三 四 五 六\t\t日 一 二 三 四 五 六\n");
						//先偷偷算單雙數月的天數
						switch(mm) {
							case 1:
								dayOfMonthOfOdd = 31;
								if(leapYear)
									dayOfMonthOfEven = 29;
								else
									dayOfMonthOfEven = 28;
								break;
							case 3:
								dayOfMonthOfOdd = 31;
								dayOfMonthOfEven = 30;
							case 5:
								dayOfMonthOfOdd = 31;
								dayOfMonthOfEven = 30;
							case 7:
								dayOfMonthOfOdd = 31;
								dayOfMonthOfEven = 31;
							case 9:
								dayOfMonthOfOdd = 30;
								dayOfMonthOfEven = 31;
							case 11:
								dayOfMonthOfOdd = 30;
								dayOfMonthOfEven = 31;
						}

						//先來找第一天
						if(mm == 1) {	// 1月和2月要特別處理
							weekOfStartOfOdd = ((yy-1)%100 + ((yy-1)%100)/4 + ((yy-1)/100)/4 - 2*((yy-1)/100) + 26*(mm+13)/10);
							weekOfStartOfEven = ((yy-1)%100 + ((yy-1)%100)/4 + ((yy-1)/100)/4 - 2*((yy-1)/100) + 26*(mm+14)/10);
						} else {
							weekOfStartOfOdd = (yy%100 + (yy%100)/4 + (yy/100)/4 - 2*(yy/100) + 26*(mm+1)/10);
							weekOfStartOfEven = (yy%100 + (yy%100)/4 + (yy/100)/4 - 2*(yy/100) + 26*(mm+2)/10);
						}

						if(weekOfStartOfOdd < 0)
							weekOfStartOfOdd = (weekOfStartOfOdd%7+7)%7;
						else
							weekOfStartOfOdd = weekOfStartOfOdd%7;

						if(weekOfStartOfEven < 0)
							weekOfStartOfEven = (weekOfStartOfEven%7+7)%7;
						else
							weekOfStartOfEven = weekOfStartOfEven%7;

						//印出萬年曆
						while(dayOdd <= dayOfMonthOfOdd || dayEven <= dayOfMonthOfEven) {	//控制列
							for(week = 0;week < 15;week++) {

								//先印單數月的部分
								if(week < 7) {
									if(dayOdd == 1) {
										if(week == weekOfStartOfOdd) {
											printf("01 ");
											dayOdd++;
										}
										else
											printf("   ");
									}
									else if(dayOdd <= dayOfMonthOfOdd) {
										if(dayOdd < 10)
											printf("0");
										printf("%d",dayOdd);
										if(week < 6)
											printf(" ");
										dayOdd++;
									}
									//處理提早結束的問題
									if(dayOdd > dayOfMonthOfOdd) {
										if(week < 6)
											printf("   ");
										else
											printf("  ");
									}
								}

								//雙數月的部分
								else if(week > 7) {
									if(dayEven == 1) {
										if(week - 8 == weekOfStartOfEven) {
											printf("01 ");
											dayEven++;
										}
										else
											printf("   ");
									}
									else if(dayEven <= dayOfMonthOfEven) {
										if(dayEven < 10)
											printf("0");
										printf("%d",dayEven);
										if(week < 15)
											printf(" ");
										dayEven++;
									}
								}
								else	//兩個月中間空兩個tab
									printf("\t\t");
							}
							printf("\n");
						}
					}
					printf("\n");
				}
			}
				
		}
		else
			printf("選擇錯誤,請重新輸入!\n\n");
	}
	system("pause");
}

沒有留言:

張貼留言