最近一個同學在寫個銀行管理系統,然後問我怎麼從文件搜索帳戶,給了方法後又不懂文件裡面的密碼怎麼與輸入的匹配。一般來說,高效的做法是用鏈表實現。用數組實現不是高效的方法,而且浪費空間。再者,對於賬戶類有個人信息集合的,一般用結構體,代碼寫起來也方便簡單。
但是他卻用數組來做,而且沒有用結構體。對於這種情況下如何搜索帳戶,如何驗證密碼呢? 我嘗試了一下,發現不難解決。
解決這個問題的方法:用文件數據的排序定位來做。
第一步:文件信息讀入
用fscanf 實現文件讀出文件信息。
1 char a[20]; 2 3 FILE* fp; 4 fp = fopen("test.txt","rb"); 5 fscanf (fp, "%s", a); 6 fclose(fp);
fcanf讀取數據,以空格為分割點。比如對於文件內容為“abc ABC”(雙引號之內)的情況,用如下代碼:
fscanf (fp, "%s", a); fscanf (fp, "%s", b);
得到字符數組a 為abc;字符數組b為 ABC;
若文件內容為“abcABC”(雙引號之內)的情況,即abc和ABC之間沒有空格隔開,用以上代碼,得到的結果將是:
字符數組a 為abcABC;字符數組b 為亂碼。
fscanf();還有一個特點,就是在一個程序裡面是順序讀入的。
在此舉個例子:
txt文件(每個數據一行)內容:
abc
ABC
ruby
然後執行下面的代碼:
int main() { char a[20],b[20],c[20]; FILE* fp; fp = fopen("test.txt","rb"); fscanf(fp, "%s", a); //……此處省略n行代碼 fscanf(fp, "%s",b); //……此處省略n行代碼 fscanf(fp, "%s",c); fclose(fp); return 0; }
得到的結果是:
字符數組a為 abc,b為ABC,c為ruby;
第二步:搜索賬號
賬號搜索的方法是遍歷文件數據,找到與輸入匹配的賬號就停止搜索。
用 while(fscanf(fp,"%s",a) > 0) 實現遍歷文件數據。
作用是把文件內容一行一行讀入賦值給字符數組a,然後再與輸入的賬號進行比較。
同時使用標記 flag 判斷是否找到匹配的賬號,以便後續處理各種不同情況。
代碼如下:
char a[20]; char shuRu[20] = {'\0'}; //輸入 int flag = 0; cout << "請輸入賬號名: "; cin >> shuRu; FILE* fp; fp = fopen("test.txt","rb"); while(fscanf(fp,"%s",a) > 0) //遍歷文件數據 { if (strcmp(shuRu,a) == 0) { cout << "找到匹配賬號" << endl; flag = 1; break; } } if (flag == 0){ cout << "用戶不存在,請注冊!" << endl; //下一步 } else //下一步; fclose(fp);
代碼中判斷數據相同用strcmp(str1,str2); 如果兩個字符數組存儲的內容相同,則 strcmp(str1,str2)== 0
此時停止搜索,用break;跳出循環。
現在舉例一下:
其中第三行為賬號,第五行為密碼。其他的為姓名,地址,年齡等其他信息。
運行如下:
第三步:定位文件密碼數據
如果是用結構體,當檢測到帳號的時候,再用結構體的 “.” 也就是 “點”密碼 來解決。簡單方便。但是用的不是結構體,所以只能用其他方法。隨筆開頭寫了用“排序定位”來做,如何實現?
從這個文件數據來看,賬號與密碼分別是第三行, 第五行,中間隔了個第四行。下面另一個帳戶也是同樣的排序。
那麼就定位到第五行,然後再進行 輸入密碼 與 文件數據的比較。
代碼如下:
if (strcmp(shuRu,a) == 0) { flag = 1; fscanf(fp,"%s",a); fscanf(fp,"%s",a); break; }
這裡面當檢測到帳號的時 flag = 1;表示找到匹配賬號。
然後用了 兩個fscanf(fp,"%s",a); 這不是代碼錯誤,前面提到了fscanf(); 是順序讀入,並且舉了 a,b,c三個字符數組的例子。
這裡再說明一下為何用兩個fscanf();
第一個fscanf();是把賬號下面的第一個數據賦值給了 字符數組a;
第二個fscanf();是把賬號下面的第二個數據賦值給了 字符數組a;
由於密碼數據是賬號數據下面的第二個數據,所以必須用兩個fscanf(); 因為fscanf();為順序讀入,無法進行跳躍。
第四步:驗證密碼
定位了密碼數據,那麼就可以進行密碼驗證了。為了實現當密碼輸入錯誤時,能重新輸入,我們必須把驗證密碼這個環節寫成一個函數,然後自身循環調用,類似遞歸的用法。
代碼如下:
void checkKey(char a[20]) { char mima[20]; cout << "輸入密碼:"; cin >> mima; if (strcmp(a, mima) == 0) cout << "登錄成功" << endl; else { cout << "密碼輸入錯誤!請重新輸入。" << endl; Sleep(2000); checkKey(a); } return; }
第五步:demo運行
初步完成了這個功能,現在把完整代碼貼出來。
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstring> 4 #include <windows.h> 5 using namespace std; 6 7 //檢測密碼 8 void checkKey(char a[20]) 9 { 10 char mima[20]; 11 cout << "輸入密碼:"; 12 cin >> mima; 13 if (strcmp(a, mima) == 0) 14 cout << "登錄成功" << endl; 15 else 16 { 17 cout << "密碼輸入錯誤!請重新輸入。" << endl; 18 Sleep(2000); 19 checkKey(a); 20 } 21 22 return; 23 } 24 25 int main() 26 { 27 char a[20]; 28 char shuRu[20] = {'\0'}; 29 30 int flag = 0; 31 32 cout << "請輸入賬號名: "; 33 cin >> shuRu; 34 35 FILE* fp; 36 fp = fopen("test.txt","rb"); 37 38 while(fscanf(fp,"%s",a) > 0) 39 { 40 41 if (strcmp(shuRu,a) == 0) 42 { 43 flag = 1; 44 fscanf(fp,"%s",a); 45 fscanf(fp,"%s",a); 46 break; 47 48 } 49 50 } 51 if (flag == 0) 52 cout << "用戶不存在,請注冊!" << endl; 53 else 54 checkKey(a); 55 56 fclose(fp); 57 return 0; 58 }
我們來運行一下。
首先文件數據如下:(每個帳戶的第三行為帳號,第五行為密碼)
運行結果:
第六步:bug修復
看上去好像完成了相應預期功能,但是細心觀察,不難發現一個bug。舉例說明一下:
當文件內容為:
可以看出,第一個帳戶的密碼和第二個帳戶的賬號相同,都是aabbcc,此時程序運行就有錯誤,當搜索到了aabbcc,程序就把他當成了賬號,於是出錯。
修復bug也很簡單,用一種特殊字符對賬號進行處理。比如在賬號後面追加一個 “@”。
因此,程序要修改兩個地方
1. 帳戶注冊後把信息寫入文件時,在賬號後面追加一個 “@”
2. 登錄時,當輸入賬號完畢後,也給輸入的賬號後面追加一個“@”
第一個地方不是我們討論的范圍,我們來看看與本篇隨筆有關的要修改的第二個地方:
相關函數: strcat(str1,str2);實現把字符數組 str2 追加到 str1 後面。
代碼如下:
cout << "請輸入賬號名: "; cin >> shuRu; strcat(shuRu,"@");
所以,當你登錄時,輸入賬號完成按回車鍵時,程序會自動給你輸入的賬號後面追加一個字符”@“,然後再與文件數據進行比較。
修改後的完整代碼如下:
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstring> 4 #include <windows.h> 5 using namespace std; 6 7 //檢測密碼 8 void checkKey(char a[20]) 9 { 10 char mima[20]; 11 cout << "輸入密碼:"; 12 cin >> mima; 13 if (strcmp(a, mima) == 0) 14 cout << "登錄成功" << endl; 15 else 16 { 17 cout << "密碼輸入錯誤!請重新輸入。" << endl; 18 Sleep(2000); 19 checkKey(a); 20 } 21 22 return; 23 } 24 25 int main() 26 { 27 char a[20]; 28 char shuRu[20] = {'\0'}; 29 30 int flag = 0; 31 32 cout << "請輸入賬號名: "; 33 cin >> shuRu; 34 strcat(shuRu,"@"); 35 36 FILE* fp; 37 fp = fopen("test.txt","rb"); 38 39 while(fscanf(fp,"%s",a) > 0) 40 { 41 42 if (strcmp(shuRu,a) == 0) 43 { 44 flag = 1; 45 fscanf(fp,"%s",a); 46 fscanf(fp,"%s",a); 47 break; 48 49 } 50 51 } 52 if (flag == 0) 53 cout << "用戶不存在,請注冊!" << endl; 54 55 else 56 checkKey(a); 57 58 fclose(fp); 59 return 0; 60 }
文件數據:
運行結果:
密碼匹配成功。
總結:對於沒有用鏈表 + 結構體的來寫帳戶登記的程序,屬於純文件信息處理。那麼就只能差強人意的用一些方法來解決。用的是”排序定位“的方法。
代碼是追求高效,簡潔的。一開始沒有用好的方法去解決,雖然也能通向羅馬,從程序的維護和更新的角度來看,是不推薦的。
1樓的 好像沒讀懂題目..
設:拿到的帳號變量為 u 密碼變量為 p
....
sql = "select user,pass from admin where user='" & u & "'"
set rs = conn.execute(sql)
if rs.eof then
response.write("找不到該用戶!")
response.end
end if
dim check,arr
check = false
arr = split(rs("pass"),"|")
for i = 0 to ubound(arr)
if p=arr(i) then
check = true
exit for
end if
next
if not check then
response.write("密碼錯誤")
response.end
end if
'這裡就是 帳號密碼都通過後執行的........
public class Util{ public static void main(String[] args){ java.util.Scanner sc = new java.util.Scanner(System.in); String[] arr = new String[5]; for(int i = 0; i < arr.length; i++){ arr[i] = sc.next(); } //這裡使用util.Arrays的代碼輸出數組 System.out.println(java.util.Arrays.toString(arr)); }}
孫成
[權威專家]