01 //p60.c -- 我拆我拆..我拆拆拆! @CopyLeft by tsaiwn@cs.nctu.edu.tw 02 // 身分證號碼檢查器與產生器 03 /// 建議先把身分證號碼規則弄清楚, 最好先手動檢查自己身分證號! 04 /// 這個是讓你先有個 "該怎麼做" 的概念 05 /// 如果看了會寫, 阿就寫啊; 如果還是不會呢? 那就 .. 看 p61.c 06 // 請拿那個p61.c去改, 寫入學號姓名, 並且註明最後修改時間; 07 /// 並且在心得部分除了討論你寫的外, 也要討論從我寫的部份學到甚麼!! 08 /// 請找有 todo 字眼的地方修改或補充就可以了 ! 09 /// 要改其它地方也可以 :-) 目前此程式是可以 Run 的, 但不完整 10 // 測試時, 請故意輸入 999 或 gen 看看會怎樣? 11 /// 學號: 12 /// 姓名: 13 /// Last modified date and time: 14 /// Original creation date and time: 2010/10/17 11:15 Sunday 15 /// Original author: tsaiwn@csie.nctu.edu.tw 16 ////// ### rand( )在 ; is???, to??? 在 17 #include 18 #include 19 #include 20 #include 21 /// 用 macro 定義一些符號常數(Symbolic constant) 備用且方便改! 22 /// 注意, Symbolic constant 一般是用全部大寫, 字_與_字間可用 _ 23 /// 例如 以下是說以後看到 HELP 都會被compiler換成 "help" 這字串 24 #define HELP "HELP" 25 /// 寫一些小工具函數 (utility functions)幫忙處理字串 26 void chop(char*); // remove tail NewLine; 去掉最尾巴的 '\n' 27 void squeeze(char*); // remove white space; 去掉所有空白 28 void toUpper(char*); // pre_process it so that easy to handle 29 /// ^^^ 包括 call squeeze 擠掉空白, 再將各字母都轉為大寫 30 /// /// declare functions that will be used 31 int errCode(char*id); // error code of id, 0 means no error 32 void pErrorMSG(char*, int); // print out error message for id 33 char* genID(char*); // generate a valid ID 34 int isHelp(char*), isQuit(char*), isGen(char*); // boolean function 35 void pHelp( ); // Help message 36 int main( ) { // 大多數 main program 都這樣開頭 37 int er; // for error code 38 static char id[99]; // 不會打那麼多字吧 ! 39 static char tmps[99]; // temporary string for id 40 for( ;; ) { // (注意) Loop forever 41 printf("ID checker, type ID: "); 42 fgets(id, sizeof(id), stdin); 43 if(feof(stdin)) break; // EOF encounted! say byebye 44 // 用 chop 把 id 尾巴的 '\n' 咬掉 45 // 把 id copy 到 tmps // 可用 strcpy() 或自己寫 :-) 46 // 把 tmps 內各字母轉為大寫 47 if(isQuit(tmps)) break; 48 if(isHelp(tmps)) {pHelp( ); continue;} 49 if(isGen(tmps)) { genID(tmps); continue; } 50 er = errCode(tmps); // find error code of the id in tmps 51 pErrorMSG(id, er); // pass the original input, error code 52 } // for(;; 53 printf("Bye bye!\nHit RETURN key ..."); 54 getchar( ); // 企圖讀取一個 char, 讓程式停著等 User 按 RETURN 鍵 55 return 0; // 告知作業系統(OS)表示我們這主程式正常結束 56 }// main( 57 ///////////////////////////////////////// 58 void chop(char*p) { // remove tail '\n' ; by tsaiwn@csie.nctu 59 if(p[0] == 0) return; // NULL string 60 while(p[0] != 0) ++p; // 到字串結束的 NULL char; p[0]就是 *p 61 --p; // move back to the last char 62 if(*p == '\n') *p = 0; // remove the newLine if it is 63 }// chop( 64 void squeeze(char* p) { // 去掉所有空白; by tsaiwn@csie.nctu 65 char* p2 = p; // p2 points to first char 66 if(p[0] == 0) return; // NULL string 67 while(*p != 0) { // Loop till end of the string 68 if( ! isspace(*p) ) { *p2 = *p; ++p2; } //保留非空白字 69 ++p; // move to next char for check 70 }// while( 71 *p2 = 0; // 使字串結束 ! Important !!! 72 }// squeeze( 73 void toUpper(char*s) { 74 int i; 75 squeeze(s); // 擠掉空白 76 for(i=0; i < strlen(s); ++i) s[i] = toupper(s[i]); // library 77 // do NOT forget to include 78 }// toUpper 79 int isHelp(char*p) { 80 if(p[0] == '?') return 1; // question mark for help 81 if( strcmp(p, HELP) == 0) return 1; //在 82 return 0; // 注意 strcmp 傳回 0 表示兩字串相等 83 } 84 int isQuit(char*p) { 85 // 參考 isHelp(char*p) 檢查 是否 "quit", "-1" 86 return 0; 87 } 88 int isGen(char*p) { // check to see if he want to generate a ID 89 // 參考 isHelp(char*p) 檢查 是否 "999", gen", "generate" 90 return 0; 91 } // isGen( 92 /// 93 void pErrorMSG(char*id, int e) { // the array msg[][]也可寫外面 94 static char msg[ ][66] ={ "OK 正確!", // 0 means OK 95 "Letter?第一字須字母", // 1 means the first char error 96 "Sex性別錯誤", "too short 太短了!", // 2 sex code error 97 "too long 太長了!怎會有那麼多碼?", // 4 means too long 98 "nonDigit? 怎會有非數字?", // contains non-digit 99 "check code?檢查碼有問題啦!" }; // 注意尾巴要有分號 ; 100 printf(" %s : %s\n", id, msg[e]); // e 是 error code 101 //若嫌太簡單, 改為可印出所有查出的錯誤 .. 102 // 可利用各 bit 代表不同 error(1, 2, 4, 8, 16, 32) 103 // 每種錯誤用 e & (2的次方) 查看是否該印, 104 // 當然, 這樣那個 errCode( ) 也要改; 可用 | bitwise or 105 // 但因各 bit 獨立, 所以算 e 的時候也可用 + 取代 | 106 }// pErrorMSG 107 void pHelp( ) { // print Help message 108 }// pHelp 109 ////////////////////////// 110 /// === a table for looking up from 'A'==> 10,.. == /// /// 111 int yy[26]={10, 11, 12, 13, 14, 15, 16, 17, // ABCDEFGH 112 34, 18, 19, 20, 21, 22, 35 // I JKLMN O 113 ,23, 24, 25, 26, 27, 28, 29 //PQRSTUV 114 ,32, 30, 31, 33};;; // W(金門縣) X Y Z(連江縣) 115 int a2nn(int x) { // 'A' ==> 10, 'B' ==> 11, ...可用上面 yy[ ] 116 int i, ans=0; 117 // this function 負責把字母轉為兩位數 118 // 建議先把 x 轉為大寫再處理, 可用 toupper(x) 119 return ans; 120 } 121 int findCheckSum(char*s) { // compute the check sum 122 int ans=0; // 就用這變數啦 123 // 先把 s[0] 轉成整數, 該整數看做兩位數, 比重各為 1, 9 124 // s[1] .. s[8] 要記得轉成真正的 0 .. 9 (減去 '0' 就可) 125 // 然後其比重依序是 8, 7, 6, 5, 4, 3, 2, 1 126 // 各自乘上比重後, 加起來, 然後除以 10 取餘數 (就是 %10) 127 // 用 10 減去該數, 若答案是 10 則改為 0, 128 // 回傳該數就是了 129 return ans; // 回傳 check sum 130 } 131 int errCode(char*id) { // return error code of the id 132 int i, len = strlen(id); // length of id 133 // 用 isalpha( ) 查 id[0], 若有錯 return 1 134 if( id[1] != '1' && id[1] != '2') return 2; // error Sex 135 // 長度太短回傳 3; 太長回傳 4 136 // 用 for Loop 查 s[2] .. s[9] 看有沒非數字, 可用 isdigit( ) 137 // 只要發現有 NOT digit 則回傳 5 138 // 叫用 findCheckSum(id); 算出 check sum 139 // 若該 check sum 與 id[9] 若不符合則回傳 6 140 /// 注意 2 與 '2' 是不同的; 2 + '0' 會等於 '2' 141 return 0; // nothing error if we did NOT find any error :-) 142 } // errCode 143 char* genID(char*p) { // 依據 p[ ] 指示來產生合法 ID 144 int i, k, c; 145 static char newID[33] = "...not yet"; // 生好的 ID 放這 146 // default 也要前面兩個 char (出生地 + Sex code) 147 // 要用到 int rand( ); 亂數函數, 宣告在 148 // 生出左邊從字母, 性別, 流水號 共 9 碼 149 // 叫用 findCheckSum(newID); 算出 檢查碼放 k; 是 0..9 喔! 150 // newID[9] = '0' + k; // 檢查碼; 注意 '0' + 6 就是 '6' 151 printf(" Valid ID for you: %s\n", newID); 152 return newID; // 回傳該 id, 用不用隨你 :-) 153 } 154 //////////////////////////////////////////////////////////// 155 /****** 156 D:\testc>path c:\Dev-Cpp\bin;%path% 157 D:\testc>gcc p61.c 158 D:\testc>a.exe 159 ID checker, type ID: 160 161 D:\testc> 162 **********************/