//p7ok.c -- 我拆我拆..我拆拆拆! @CopyLeft by tsaiwn@cs.nctu.edu.tw /// 第七週查出所有錯誤以及產生合法ID, /// 可指定第一碼(出生地), 也可指定男或女 // 身分證號碼檢查器與產生器 // 測試時, 請故意輸入 999 或 gen 或 a2 或 a3 或 92 看看會怎樣? /// 學號: /// 姓名: /// Last modified date and time: 2010/11/05 18:00 Friday /// 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) 備用且方便改! // 比較好的方式是把這些巨集定義都搬去一個header file 再 #include 它 #define HELP "HELP" #define QUIT1 "QUIT" #define QUIT2 "-1" #define GEN1 "999" #define GEN2 "GEN" #define GEN3 "GENERATE" #define ERR_ABC 1 #define ERR_SEX 2 #define ERR_TOO_SHORT 4 #define ERR_TOO_LONG 8 #define ERR_NON_DIGIT 16 #define ERR_CHECKSUM 32 int errNUM[ ] = {ERR_ABC, ERR_SEX, ERR_TOO_SHORT ,ERR_TOO_LONG, ERR_NON_DIGIT, ERR_CHECKSUM };;; const int N_ERR = (sizeof errNUM/sizeof errNUM[0]); /// 寫一些小工具函數 (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 findErrCode(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 srand( time(0) ); // randomize 以便產生真的亂數序 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到左邊 squeeze(tmps); // 擠掉空白; squeeze out all white space toUpper(tmps); // to upper case if(isQuit(tmps)) break; if(isHelp(tmps)) {pHelp( ); continue;} if(isGen(tmps)) { genID(tmps); continue; } er = findErrCode(tmps); // find error code of the id in tmps pErrorMSG(tmps, er); // the modified 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; 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; if( strlen(p) <= 2) return 1; // 只打 0, 1, 2 字都當作要生ID return 0; } // isGen( /// void pErrorMSG(char*id, int e) { // the array msg[][]也可寫外面 static char msg[ ][66] ={ // Note ! 注意與之前只印一種error不同! "Letter?第一字須字母", // 1 means the first char error "Sex性別錯誤", // 2 sex code error "too short 太短了!", // 4 too short error "too long 太長了!怎會有那麼多碼?", // 8 means too long "nonDigit? 怎會有非數字?", //16 contains non-digit "check code? 神秘檢查碼有問題啦!" }; //32,注意尾巴要有分號 ; int i; if(e == 0) { // no error printf(" %s : OK 正確! 讚喔 !\n", id); return; }// if for(i=0; i < N_ERR; i++) { if(e & errNUM[i]) // 注意是bitwise and &, 不是 && printf(" %s : %s\n", id, msg[i]); // 注意不是 msg[e] 喔! }//for i //若嫌太簡單, 改為可印出所有查出的錯誤 .. // 可利用各 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' // Table look up ! x = x - 'A'; // 0..25 if(x<0 || x > 25) return 99; // Error, impossible !(防呆) ans = yy[x]; // 取得對應該字母的 10..35 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 ! for(i=1; i <= 8; ++i) ans += (s[i] - '0') * (9-i); ans = ans % 10; ans = 10 - ans; if(ans == 10) ans = 0; // 注意 '0' 就是 48, '1' 就是 49 (ASCII code) // 注意這算出的是 0..9; 不是 '0'..'9' // 若要用 '0'..'9'也可, 但使用此函數者要搭配改 return ans; // 回傳 check sum } int findErrCode(char*id) { // return error code of the id int i, len = strlen(id); // length of id int ans = 0; if( !isalpha(id[0]) ) ans+= ERR_ABC; // 1st char 字母Letter? if( id[1] != '1' && id[1] != '2') ans+= ERR_SEX; // error Sex if(len < 10) ans += ERR_TOO_SHORT; // too short if(len > 10) ans += ERR_TOO_LONG; // too Long for(i=2; i< len; ++i) { if( !isdigit(id[i]) ) {ans += ERR_NON_DIGIT; break; } } // 注意 !!! 只要查到一個 non-digit 就要 break! WHY ? Why? why? /// 只有剩下檢查碼不知對否? now do check the checksum if(len == 10) { i = findCheckSum(id); // 注意這算出的是 0..9; 不是 '0'..'9' if( i != (id[9]-'0') ) ans += ERR_CHECKSUM; } // check the checksum ONLY when length of ID is 10 :-) return ans; // 也可能是 0 喔 } // findErrCode 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 newID[2] = '0' + rand( )%10; // '0' .. '9' for(i=2; i<=8; ++i) { // 用 rand() 生出流水號共 7 碼 /// generate newID[2] .. newID[8] // todo ! newID[i] = '0' + rand( )%10; // '0' .. '9' }//for(i k = findCheckSum(newID); // find weighted check sum newID[9] = '0' + k; // 檢查碼 ; printf(" Valid ID for you: %s\n", newID); return newID; // 回傳該 id, 用不用隨你 :-) } //////////////////////////////////////////////////////////// /****** D:\testc>path c:\Dev-Cpp\bin;%path% D:\testc>gcc p7ok.c D:\testc>a.exe ID checker, type ID: a 123 456 789 A123456789 : OK 正確! 讚喔 ! ID checker, type ID: a 1234 5678 9 A123456789 : OK 正確! 讚喔 ! ID checker, type ID: a123 456 788 A123456788 : check code? 神秘檢查碼有問題啦! ID checker, type ID: a123 456 798 A123456798 : OK 正確! 讚喔 ! ID checker, type ID: 9999999 9999999 : Letter?第一字須字母 9999999 : Sex性別錯誤 9999999 : too short 太短了! ID checker, type ID: Valid ID for you: L124620433 ID checker, type ID: a2 Valid ID for you: A210967944 ID checker, type ID: p1 Valid ID for you: P199751035 ID checker, type ID: p Valid ID for you: P287153227 ID checker, type ID: s Valid ID for you: S266502910 ID checker, type ID: bbbb BBBB : Sex性別錯誤 BBBB : too short 太短了! BBBB : nonDigit? 怎會有非數字? ID checker, type ID: a123456 A123456 : too short 太短了! ID checker, type ID: aaaaaaaaaaaaaaaaaaaaa AAAAAAAAAAAAAAAAAAAAA : Sex性別錯誤 AAAAAAAAAAAAAAAAAAAAA : too long 太長了!怎會有那麼多碼? AAAAAAAAAAAAAAAAAAAAA : nonDigit? 怎會有非數字? ID checker, type ID: gen Valid ID for you: G132023641 ID checker, type ID: 999 Valid ID for you: Q214060378 ID checker, type ID: g9 Valid ID for you: G187141863 ID checker, type ID: g2 Valid ID for you: G258950574 ID checker, type ID: generate Valid ID for you: M128526106 ID checker, type ID: help HELP, 999, GEN, GENERATE, QUIT, -1 ID checker, type ID: -1 Bye bye! Hit RETURN key ... D:\testc> **********************/