最近一個同學在寫個銀行管理系統,然後問我怎麼從文件搜索帳戶,給了方法後又不懂文件裡面的密碼怎麼與輸入的匹配。一般來說,高效的做法是用鏈表實現。用數組實現不是高效的方法,而且浪費空間。再者,對於賬戶類有個人信息集合的,一般用結構體,代碼寫起來也方便簡單。
但是他卻用數組來做,而且沒有用結構體。對於這種情況下如何搜索帳戶,如何驗證密碼呢? 我嘗試了一下,發現不難解決。
解決這個問題的方法:用文件數據的排序定位來做。
第一步:文件信息讀入
用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)); }}
孫成
[權威專家]