//p8ok9ok.c --- 萬年曆 --- 這是完成的完整版本 // 這版本若啥都沒輸入就按 ENTER 則看作要求 help! // 算星期幾的部分已經完全正確, 且改為全部參考 0001/01/01 // 然後, 再看是否為調整日之後的日期, 是則減去多算的閏年日 // ===美國版 (1752/09 只有19天) and 中國版(1911/12只有18天) //===== 加入羅馬天主教國家版(輸入中有 r 或 R) /// 中國的西元 1911 年 12 月 也已經改好! /// 所以, 還沒做完的繼續做這題, 實在還不會, 就看看這參考答案! // @CopyLeft by tsaiwn@cs.nctu.edu.tw /// Last modified date and time: 2010/11/18 15:58 /// Original creation date and time: 2010/10/31 08:08 #include #include #include #include /// 用 macro 定義一些符號常數(Symbolic constant) 備用且方便改! // 比較好的方式是把這些巨集定義都搬去一個header file 再 #include 它 #define QUIT1 "quit" #define QUIT2 "-1" #define C_USA 0 #define C_CN 2 #define C_ROME 38 const int REF_DAY_ONE = 6; // 注意 0001/01/01 是週六 //const int REF_DAY_TWO = 0; // for C_USA 1752/10/01 Sunday //const int REF_DAY_THREE = 1; // for C_CN 1912/01/01 Monday /// 寫一些小工具函數 (utility functions)幫忙 int isQuit(char*), isHelp(char*); void chop(char*); void toLower(char*p); void check(char*), genCal( ), pHelp(void); int yyyy, mm, dd, what, country; // Global for use everywhere /// 上面這五個變數: 你的就是我的, 不必傳來傳去! /// 主程式是整個程式的總指揮! int main( ) { // 大多數 main program 都這樣開頭 int i, k; static char buf[999]; // 不會打那麼多字吧 ! for( ; ; ) { // (注意) Loop forever printf("[C|R] [[day] month] year: "); fgets(buf, sizeof buf, stdin); if(feof(stdin)) break; // EOF encounted! say byebye chop(buf); // 咬掉尾巴的 NewLine if any toLower(buf); // all Letter in Upper case into lower case if(isQuit(buf)) break; if(isHelp(buf)) { pHelp( ); continue; } // 印 HELP 後回 Loop check(buf); // check Country, day/month/year genCal( ); // generate Calendar and print it out } // for(;; printf("Bye bye!\nHit RETURN key ..."); getchar( ); // 企圖讀取一個 char, 讓程式停著等 User 按 RETURN 鍵 return 0; // 告知作業系統(OS)表示我們這主程式正常結束 }// main( ///////////////////// 把每個函數控制在一頁之內, 太多就切開分給別的函數! void check(char*p) { int i, k; country = C_USA; // asumption for(i=0; i< strlen(p); i++) { // why ONLY compare to 'c' ? if(p[i] == 'c') country = C_CN; // Chinese if(p[i] == 'r') country = C_ROME; // 遵守羅馬帝國規定的國家 if(!isdigit(p[i]) ) p[i] = ' '; // remove non-digit }//for i what = sscanf(p, "%d %d %d", &dd, &mm, &yyyy); // try to read switch(what) { // do NOT forget "break;" 'cause Falling through case 1: yyyy = dd; break; // 只有 "年 " case 2: yyyy = mm; mm=dd; if(mm>12 && yyyy < 13) { // 一定是弄錯啦 (防呆) mm = yyyy; yyyy = dd; // 幫他換過來 :-) }//if if(mm>12) mm = 1; // January (再防呆) break; // 月 年 case 3: // one day if(dd>31 && yyyy < 32) { // (防呆)把 年 日 對調 dd = dd ^ yyyy; yyyy = dd ^ yyyy; dd = dd ^ yyyy; } //if if(mm>12) mm = 1; // January (防呆 ) //printf(" Date: %4.3d/%02d/%2.2d\n", yyyy,mm,dd); break; default: // error input break; }// switch( }// check( void pYear(int), pMonth(int, int), pDate(int,int,int); void genCal( ) { // 上司管下司, 鋤頭管畚箕! if(what==1) pYear(yyyy); else if(what == 2) pMonth(yyyy, mm); else if(what == 3) pDate(yyyy, mm, dd); else printf(" ?Error input!\n"); return ; } //ggg( int isLeap(int year) { // 是閏年嗎? int bp = 1582; // 羅馬與天主教國家在 1582 switch 到儒略曆 if(country == C_USA) bp = 1752; // USA/UK/Canda/Aus. if(country == C_CN) bp = 1911; // 中國在推翻滿清後 switch if(year <= bp) return (year%4) == 0; // 舊式為每四年一閏年 if( (year%400) == 0) return 1; if( (year%100) == 0) return 0; return (year%4) == 0; // 除以四若除得盡是閏年 (1 就是 yes) }// isLeap( /// int days[ ] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /// 因從之前範例 copy/paste , /// .. 不小心弄出兩個同樣功能的 array :-) int dayTbl[ ] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // July..Dec ////// /// 以下 hehehe( ) 負責找出 year/month/dd 離 0001/01/01 有幾天 ? long hehehe(int year, int month, int dd) { // 舊式算法 (凱薩曆) //int dd = 1; // 1/1/1 算到 year/month/01 幾天 long ans = 0; int i, k; ans = 365 * (year -1); for(i=1; i< year; i++) if( isLeap(i) ) ++ans; // 閏年多一天 for(i=1; i< month; ++i) ans += dayTbl[i]; if( (month > 2) && isLeap(year) ) ++ans; //今年閏年 ans = ans + dd -1; // 這個月 1 日 (dd == 1) return ans; // 注意 01/01/01 到 01/01/01 算 0 天 }// /// /// 以下是負責找出 year/month/day 是星期幾 0..6 int findDayOfWeek(int year, int month, int day) { // find day of the week // 0 indicates Sunday, 1 == Monday, ...6 == Saturday long days = 0; // how many days from reference point till now? int ans = REF_DAY_ONE; // 參考 0001/01/01 int switchYear=1582, switchMonth=10, switchDay=4; // 10/4 then 10/15 int adjust = 10; // adjust 10 days if switch at 1582/10 if(country==C_USA) { switchYear=1752; switchMonth=9; switchDay=2; // 9/2 then 9/14 adjust = 11; // 9/14 is original 9/3 } if(country == C_CN) { switchYear=1911; switchMonth=12; switchDay=18; // 12/18 then 1/1 adjust = 13; } days = hehehe(year, month, day); if(year > switchYear) days = days - adjust; // 分開寫比較不會想錯! if( year==switchYear && month > switchMonth )days = days - adjust; if( year==switchYear &&(month==switchMonth) && day > (switchDay+adjust) ) { days = days - adjust; } ans = (ans + days%7) % 7; return ans; // 0..6 == Sunday, Mon, ... Saturday } // findDayOfWeek : find the day of the week char a[6][33], b[6][33], c[6][33]; // 各用來填寫一個月的月曆 char mName[ ][33]={"Error!", "January", "Feburary", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; /// void rome1582(char x[6][33]) { // 1582/10 for Rome sprintf(x[0]," 1 2 3 4 15 16"); sprintf(x[1],"17 18 19 20 21 22 23"); sprintf(x[2],"24 25 26 27 28 29 30 "); sprintf(x[3],"31"); x[4][0] = x[5][0] = 0; } // special processing for 1582/10 void putMonth(int y, int m, char x[6][33]){ int dayMax; // 這個月幾天, 要用查表並看是否閏年2月? char sss[5]; // for temporary usage int i, k, gg =0; // gg 是星期幾, 0 == Sunday, 1==Monday int stDate = 1; /// 開始排的日, 當然是從 每月 1 日 /// /// gg = findDayOfWeek(y, m, stDate); // 星期幾? gg = gg % 7; // 防呆 0..6 Sun, Mon, .. Saturday //sprintf(x[1], "Su Mo Tu We Th Fr Sat"); dayMax = days[m]; // 這個月有幾天, 要用查表並看是否閏年2月? if(m==2 && isLeap(y)) dayMax = 29; // 閏年ㄟ // 開始囉 for(i=0; i <= 5; i++) strcpy(x[i],""); // 空字串 // 一週有七天, Sunday 開始, 先空掉左邊 gg 天 //x[0][0] = 0; // clear string; 空字串是一開始放 '\0' 就是 0 for(k=0; k 內宣告 // 再來, 接著開始填到週六為止 ! for( ; k <= 6; k++) { // k==6 表示週六 sprintf(sss, "%2d ", stDate++); strcat(x[0], sss); if(stDate >= dayMax) break; } // 第 0 列以外的其他各列 (row 1 .. row 5) for(i=1; i <= 5; ++i ) { for( k=0; k <= 6; k++) { // k==6 表示週六 sprintf(sss, "%2d ", stDate++); strcat(x[i], sss); // 第 i 列, 每列 7 天 if(stDate > dayMax) break; // done } if(stDate > dayMax) break; }// for i /// /// 特別的年月 for Rome if(country==C_ROME && y==1582 && m == 10) { rome1582(x); // x 是那張小月曆卡片 // 不要 return喔, 因等下要把每列補為剛好 25 chars } ////// USA, UK, 1752/09 if(country==C_USA && y==1752 && m == 9) { sprintf(x[0]," 1 2 14 15 16"); sprintf(x[1],"17 18 19 20 21 22 23"); sprintf(x[2],"24 25 26 27 28 29 30 "); x[3][0] = x[4][0] = x[5][0] = 0; } /// /// /// Chinese, 1911/12 if(country==C_CN && y==1911 && m == 12) { // 中國年的特別月 sprintf(x[3],"18 "); // 這樣剛好正確! x[4][0] = x[5][0] = 0; // 這樣剛好正確!! // done } // 再來要ㄘㄨㄥ\ 啥呢? 把每列弄成固定長度 for(i=0; i< 6; ++i) { // 第 0 列到第 5 列都要這樣 k = strlen(x[i]); // length of x[i] while(k < 25) x[i][k++] = ' '; //補空白 x[i][25]=0; // 讓字串結束, length 變 25 } }//putMonth( void pMonth(int y, int m) { int i, k; putMonth(y, m, a); // into array a printf("\t\t\t %s %4d\n", mName[m], y); printf("\t\t\t Su Mo Tu We Th Fr Sat\n"); for(i=0; i < 6; i++) printf("\t\t\t %s\n", a[i]); return; } // pMonth( void pYear(int y) { int i, k; for(i=1; i<= 10; i=i+3) { putMonth(y, i, a); putMonth(y, i+1, b); putMonth(y, i+2, c); for(k=i; k <= i+2; k++) printf("\t %s %4d ", mName[k], y); printf("\n"); for(k=1; k <=3; ++k)printf("Su Mo Tu We Th Fr Sat "); printf("\n"); for(k=0; k < 6; k++) printf("%s %s %s\n", a[k], b[k], c[k]); }// for i //// } // pYear( char dName[ ][33]={ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; void pDate(int y, int m, int d) { int k = findDayOfWeek(y, m, d); k = k % 7; // 防呆; 0..6 printf("%4d/%2.2d/%02d is %s\n", y, m, d, dName[k]); } ////// int isQuit(char*p) { if( strcmp(p, QUIT1) == 0) return 1; // "quit" if( strcmp(p, QUIT2) == 0) return 1; // "-1" if( strcmp(p, "bye") == 0) return 1; if( strcmp(p, "byebye") == 0) return 1; return 0; } ////// int isHelp(char*p) { if( strcmp(p, "help") == 0) return 1; if( *p == '?' ) return 1; // 第一個字是 ? if( *p == 0 ) return 1; // empty string; strlen(p) == 0 return 0; } void chop(char*p) { // remove tail '\n' ; by tsaiwn@csie.nctu if(p[0] == 0) return; // NULL string while(p[0] != 0) ++p; // 到字串結束的 NULL char; p[0]就是 *p --p; // move back to the last char if(*p == '\n') *p = 0; // remove the newLine if it is }// chop( void toLower(char*p){ int i; for(i=0; i< strlen(p); i++) p[i]= tolower(p[i]); } // toLower void pHelp( ) { // 注意我們只用一句 printf( ) printf("This is an ID checker/generator\n" " Input \"help\" or \"?\" for this help message.\n" " 輸入身分證號碼表示要查看是否為合法的身分證號碼\n" " 輸入一個字或兩個字表示要生出身分證號碼\n" " 例如, 輸入38 或輸入 8y 都表示生出隨便一個身分證號碼\n" " 輸入 a2 表示要產生 A2 開始的身分證號碼\n" " 輸入 32 表示要產生 ?2 開始的身分證號碼(女生)\n" " 輸入 g3 或 g? 都表示要產生 G 開始的身分證號碼\n" "\n"); } //////////////////////////////////////////////////////////// /****** D:\testc>path c:\Dev-Cpp\bin;%path% D:\testc>gcc p80.c D:\testc>a.exe D:\testc>gcc p8ok9ok.c D:\testc>a.exe [C|R] [[day] month] year: 1 1 1 1/01/01 is Saturday [C|R] [[day] month] year: 10 1582 October 1582 Su Mo Tu We Th Fr Sat 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 [C|R] [[day] month] year: 10 1582 rome October 1582 Su Mo Tu We Th Fr Sat 1 2 3 4 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 [C|R] [[day] month] year: 9 1752 September 1752 Su Mo Tu We Th Fr Sat 1 2 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 [C|R] [[day] month] year: 1752 9 September 1752 Su Mo Tu We Th Fr Sat 1 2 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 [C|R] [[day] month] year: 1752 9 ch September 1752 Su Mo Tu We Th Fr Sat 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 [C|R] [[day] month] year: 1911 12 December 1911 Su Mo Tu We Th Fr Sat 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 [C|R] [[day] month] year: 12 1911 chinese December 1911 Su Mo Tu We Th Fr Sat 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <== 注意這已經 OK [C|R] [[day] month] year: 1 1912 January 1912 Su Mo Tu We Th Fr Sat 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 [C|R] [[day] month] year: 18 12 1911 1911/12/18 is Monday [C|R] [[day] month] year: 1911 12 18 c 1911/12/18 is Sunday [C|R] [[day] month] year: 18 12 1911 c 1911/12/18 is Sunday [C|R] [[day] month] year: 1912 1 1 1912/01/01 is Monday [C|R] [[day] month] year: 1 1 1912 1912/01/01 is Monday [C|R] [[day] month] year: 1 1 1912 c 1912/01/01 is Monday [C|R] [[day] month] year: 1 1 1912 ro 1912/01/01 is Monday [C|R] [[day] month] year: 1752 9 2 1752/09/02 is Wednesday [C|R] [[day] month] year: 1752 9 14 1752/09/14 is Thursday [C|R] [[day] month] year: 1752 9 14 c 1752/09/14 is Monday [C|R] [[day] month] year: 1752 9 2 r 1752/09/02 is Saturday [C|R] [[day] month] year: 1752 January 1752 Feburary 1752 March 1752 Su Mo Tu We Th Fr Sat Su Mo Tu We Th Fr Sat Su Mo Tu We Th Fr Sat 1 2 3 4 1 1 2 3 4 5 6 7 5 6 7 8 9 10 11 2 3 4 5 6 7 8 8 9 10 11 12 13 14 12 13 14 15 16 17 18 9 10 11 12 13 14 15 15 16 17 18 19 20 21 19 20 21 22 23 24 25 16 17 18 19 20 21 22 22 23 24 25 26 27 28 26 27 28 29 30 31 23 24 25 26 27 28 29 29 30 31 April 1752 May 1752 June 1752 Su Mo Tu We Th Fr Sat Su Mo Tu We Th Fr Sat Su Mo Tu We Th Fr Sat 1 2 3 4 1 2 1 2 3 4 5 6 5 6 7 8 9 10 11 3 4 5 6 7 8 9 7 8 9 10 11 12 13 12 13 14 15 16 17 18 10 11 12 13 14 15 16 14 15 16 17 18 19 20 19 20 21 22 23 24 25 17 18 19 20 21 22 23 21 22 23 24 25 26 27 26 27 28 29 30 24 25 26 27 28 29 30 28 29 30 31 July 1752 August 1752 September 1752 Su Mo Tu We Th Fr Sat Su Mo Tu We Th Fr Sat Su Mo Tu We Th Fr Sat 1 2 3 4 1 1 2 14 15 16 5 6 7 8 9 10 11 2 3 4 5 6 7 8 17 18 19 20 21 22 23 12 13 14 15 16 17 18 9 10 11 12 13 14 15 24 25 26 27 28 29 30 19 20 21 22 23 24 25 16 17 18 19 20 21 22 26 27 28 29 30 31 23 24 25 26 27 28 29 30 31 October 1752 November 1752 December 1752 Su Mo Tu We Th Fr Sat Su Mo Tu We Th Fr Sat Su Mo Tu We Th Fr Sat 1 2 3 4 5 6 7 1 2 3 4 1 2 8 9 10 11 12 13 14 5 6 7 8 9 10 11 3 4 5 6 7 8 9 15 16 17 18 19 20 21 12 13 14 15 16 17 18 10 11 12 13 14 15 16 22 23 24 25 26 27 28 19 20 21 22 23 24 25 17 18 19 20 21 22 23 29 30 31 26 27 28 29 30 24 25 26 27 28 29 30 31 [C|R] [[day] month] year: -1 Bye bye! Hit RETURN key ... D:\testc> **********************/