//p61.c -- 我拆我拆..我拆拆拆! @CopyLeft by tsaiwn@cs.nctu.edu.tw // 身分證號碼檢查器與產生器 /// 建議先把身分證號碼規則弄清楚, 最好先手動檢查自己身分證號! // 請拿這個p61.c去改, 寫入學號姓名, 並且註明最後修改時間; /// 並且在心得部分除了討論你寫的外, 也要討論從我寫的部份學到甚麼!! /// 請找有 todo 字眼的地方修改或補充就可以了 ! /// 要改其它地方也可以 :-) 目前此程式是可以 Run 的, 但不完整 // 測試時, 請故意輸入 999 或 gen 看看會怎樣? /// 學號: /// 姓名: /// Last modified date and time: /// Original creation date and time: 2010/10/17 11:15 Sunday /// Original author: tsaiwn@csie.nctu.edu.tw ////// ### rand( )在 ; is???, to??? 在 #include #include #include #include /// 用 macro 定義一些符號常數(Symbolic constant) 備用且方便改! #define HELP "HELP" #define QUIT1 "QUIT" #define QUIT2 "-1" #define GEN1 "999" #define GEN2 "GEN" #define GEN3 "GENERATE" /// 寫一些小工具函數 (utility functions)幫忙處理字串 void chop(char*); // remove tail NewLine; 去掉最尾巴的 '\n' void squeeze(char*); // remove white space; 去掉所有空白 void toUpper(char*); // pre_process it so that easy to handle /// ^^^ 包括 call squeeze 擠掉空白, 再將各字母轉為大寫 /// /// declare functions that will be used int errCode(char*id); // error code of id, 0 means no error void pErrorMSG(char*, int); // print out error message for id char* genID(char*); // generate a valid ID int isHelp(char*), isQuit(char*), isGen(char*); // boolean function void pHelp( ); // Help message int main( ) { // 大多數 main program 都這樣開頭 int er; // for error code static char id[99]; // 不會打那麼多字吧 ! static char tmps[99]; // temporary string for id for( ;; ) { // (注意) Loop forever printf("ID checker, type ID: "); fgets(id, sizeof(id), stdin); if(feof(stdin)) break; // EOF encounted! say byebye chop(id); // 去掉最尾巴的 '\n' strcpy(tmps, id); // copy id into tmps; 注意是右邊copy到左邊 toUpper(tmps); // to upper and condense it if(isQuit(tmps)) break; if(isHelp(tmps)) {pHelp( ); continue;} if(isGen(tmps)) { genID(tmps); continue; } er = errCode(tmps); // find error code of the id in tmps pErrorMSG(id, er); // pass the original input, error code } // for(;; printf("Bye bye!\nHit RETURN key ..."); getchar( ); // 企圖讀取一個 char, 讓程式停著等 User 按 RETURN 鍵 return 0; // 告知作業系統(OS)表示我們這主程式正常結束 }// main( ///////////////////////////////////////// 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 squeeze(char* p) { // 去掉所有空白; by tsaiwn@csie.nctu char* p2 = p; // p2 points to first char if(p[0] == 0) return; // NULL string while(*p != 0) { // Loop till end of the string if( ! isspace(*p) ) { *p2 = *p; ++p2; } //保留非空白字 ++p; // move to next char for check }// while( *p2 = 0; // 使字串結束 ! Important !!! }// squeeze( void toUpper(char*s) { int i; squeeze(s); // 擠掉空白 for(i=0; i < strlen(s); ++i) s[i] = toupper(s[i]); // library // do NOT forget to include }// toUpper int isHelp(char*p) { if(p[0] == '?') return 1; // question mark for help if( strcmp(p, HELP) == 0) return 1; //在 return 0; // 注意 strcmp 傳回 0 表示兩字串相等 } int isQuit(char*p) { if( strcmp(p, QUIT1) == 0) return 1; // "quit" if( strcmp(p, QUIT2) == 0) return 1; // "-1" return 0; } int isGen(char*p) { // check to see if he want to generate a ID if( strcmp(p, GEN1) == 0) return 1; if( strcmp(p, GEN2) == 0) return 1; if( strcmp(p, GEN3) == 0 ) return 1; return 0; } // isGen( /// void pErrorMSG(char*id, int e) { // the array msg[][]也可寫外面 static char msg[ ][66] ={ "OK 正確!", // 0 means OK "Letter?第一字須字母", // 1 means the first char error "Sex性別錯誤", "too short 太短了!", // 2 sex code error "too long 太長了!怎會有那麼多碼?", // 4 means too long "nonDigit? 怎會有非數字?", // contains non-digit "check code?檢查碼有問題啦!" }; // 注意尾巴要有分號 ; printf(" %s : %s\n", id, msg[e]); // e 是 error code //若嫌太簡單, 改為可印出所有查出的錯誤 .. // 可利用各 bit 代表不同 error(1, 2, 4, 8, 16, 32) // 每種錯誤用 e & (2的次方) 查看是否該印, // 當然, 這樣那個 errCode( ) 也要改; 可用 | bitwise or // 但因各 bit 獨立, 所以算 e 的時候也可用 + 取代 | }// pErrorMSG void pHelp( ) { // print Help message printf(" %s, %s, %s, %s, %s, -1\n", HELP, GEN1, GEN2, GEN3, QUIT1, QUIT2); }// pHelp ////////////////////////// /// === a table for looking up from 'A'==> 10,.. == /// /// int yy[26]={10, 11, 12, 13, 14, 15, 16, 17, // ABCDEFGH 34, 18, 19, 20, 21, 22, 35 // I JKLMN O ,23, 24, 25, 26, 27, 28, 29 //PQRSTUV ,32, 30, 31, 33};;; // W(金門縣) X Y Z(連江縣) int a2nn(int x) { // 'A' ==> 10, 'B' ==> 11, ... int i, ans = 0; // this function 負責把字母轉為兩位數 x = toupper(x); // ensure it is in Uppercase 'A'..'Z' /// 以下兩句 if 只是 demo 說也可以這樣分段做 if(x=='A') return 10; if(x <= 'H') return 10 +(x - 'A'); // 'A'..'H' // use table look up ! todo ! // todo return ans; } int findCheckSum(char*s) { // compute the check sum int i, ans=0; ans = a2nn(s[0]); // 'A' ==> 10, 'B' ==> 11, ... ans = ans/10 + 9*(ans%10); // weight: 1, 9, 8, 7, .., 1 // use for loop; //todo ! // todo ! // 注意 '0' 就是 48, '1' 就是 49 (ASCII code) // 注意這算出的是 0..9; 不是 '0'..'9' // 若要用 '0'..'9'也可, 但使用此函數者要搭配改 return ans; // 回傳 check sum } int errCode(char*id) { // return error code of the id int i, len = strlen(id); // length of id if( !isalpha(id[0]) ) return 1; // 1st char 字母Letter? if( id[1] != '1' && id[1] != '2') return 2; // error Sex if(len < 10) return 3; // too short if(len > 10) return 4; // too Long for(i=2; i< len; ++i) if( !isdigit(id[i]) ) return 5; /// 只有剩下檢查碼不知對否? now do check the checksum i = findCheckSum(id); // 注意這算出的是 0..9; 不是 '0'..'9' //if( i != (id[9]-'0') ) return 6; // error check code? // ^^^ 注意寫好了要把上列 enable (暫時 say OK :-) return 0; // nothing error } // errCode char* genID(char*p) { // 依據 p[ ] 指示來產生合法 ID int i, k, c; static char newID[33] = "...not yet"; // 生好的 ID 放這 // default 也要前面兩個 char (出生地 + Sex code) newID[0] = 'A' + rand( )%26; // 'A'..'Z' newID[1] = '1' + rand( )%2; // '1' .. '2' c = p[0]; // 看看 第一字(出生地)要不要指定? 但要注意合法! if(strlen(p)==1) { if(isalpha(c)) newID[0] = toupper(c); // 要防呆 ! }else if(strlen(p)==2) { if(isalpha(c)) newID[0] = toupper(c); // 要防呆 ! c = p[1]; // obtain the sex code if(c=='1' || c=='2') newID[1] = c; // 防呆! 正確才放入! } // if for(i=2; i<=8; ++i) { // 用 rand() 生出流水號共 7 碼 /// generate newID[2] .. newID[8] // todo ! // todo! }//for(i k = findCheckSum(newID); // find weighted check sum // newID[9] = '0' + k; // 檢查碼 ; /// 注意寫好要把上列 enable ! printf(" Valid ID for you: %s\n", newID); return newID; // 回傳該 id, 用不用隨你 :-) } //////////////////////////////////////////////////////////// /****** D:\testc>path c:\Dev-Cpp\bin;%path% D:\testc>gcc p61.c D:\testc>a.exe ID checker, type ID: ? help, 999, gen, generate, quit, -1 ID checker, type ID: help help, 999, gen, generate, quit, -1 ID checker, type ID: ggyy ggyy : Sex性別錯誤 ID checker, type ID: 3388 3388 : Letter?第一字須字母 ID checker, type ID: a389 a389 : Sex性別錯誤 ID checker, type ID: a12345 a12345 : too short 太短了! ID checker, type ID: a1234567899999 a1234567899999 : too long 太長了!怎會有那麼多碼? ID checker, type ID: a123 456 789 a123 456 789 : OK 正確! ID checker, type ID: A123456 789 A123456 789 : OK 正確! ID checker, type ID: 999 Valid ID for you: P2.not yet ID checker, type ID: gen Valid ID for you: Q1.not yet ID checker, type ID: gen Valid ID for you: H1.not yet ID checker, type ID: ahaha ahaha : Sex性別錯誤 ID checker, type ID: a123gg a123gg : too short 太短了! ID checker, type ID: a123gg6789 a123gg6789 : nonDigit? 怎會有非數字? ID checker, type ID: Q123456789 Q123456789 : OK 正確! ID checker, type ID: -1 Bye bye! Hit RETURN key ... D:\testc> **********************/