280 likes | 425 Views
補充教材. 串流輸入輸出的錯誤狀態. 簡介. 標準串流輸入輸出與檔案串流輸入輸出物件皆包含成員變數用來紀錄串流輸入輸出的狀態。 這些變數稱作「狀態位元」( State bit )。 eofbit : true 代表遇到 EOF 字元。 failbit : true 代表輸入格式不正確。 badbit : true 代表輸入或輸出失敗。 任何不可回覆的錯誤產生,例如資料遺失。 goodbit :輸入輸入一切正常(換言之,以上的變數都沒有被設為 true ), goodbit 才會被設成 true 。 注意事項:
E N D
簡介 • 標準串流輸入輸出與檔案串流輸入輸出物件皆包含成員變數用來紀錄串流輸入輸出的狀態。 • 這些變數稱作「狀態位元」(State bit)。 • eofbit:true代表遇到EOF字元。 • failbit:true代表輸入格式不正確。 • badbit:true代表輸入或輸出失敗。 • 任何不可回覆的錯誤產生,例如資料遺失。 • goodbit:輸入輸入一切正常(換言之,以上的變數都沒有被設為true),goodbit才會被設成true。 • 注意事項: • 一旦eofbit、failbit或badbit被設為true,則該串流物件的輸入輸出將因此而失效。
成員函式eof()、fail()、bad()與good() • 呼叫串流物件的eof()、fail()、bad()與good()等成員函式即可回傳各狀態位元的值 • 回傳true或false。
注意事項 • 注意事項: • 一旦eofbit、failbit或badbit被設為true,則該串流物件的輸入輸出皆會因此失效。 • 之後再以同樣的串流物件進行輸入輸出將會得到不正常的結果。 • 問題:如何清除串流物件的異常狀態呢?
成員函式clear() • 功能:接受一個引數,將特定的狀態位元予以清除。 • 例如: cin.clear( ios::failbit ); • 如不給引數,則預設會將所有的狀態位元予以清除(設為false),並將goodbit設為true。 • 範例 cin.clear();
簡介 • 利用狀態位元我們可以用來檢查使用者輸入的格式是否正確。 • 當利用串流物件輸入時,檢查物件的狀態位元;一旦輸入失敗,即捨棄所有的串流輸入,並將狀態回復。
成員函式ignore() • 串流輸入物件中提供成員函式ignore(),其功能為: • 讀取串流輸入並將字元捨棄。 • 提供一個正整數與分界字元為引數,代表捨棄的字元數目或終止的字元。 • 預設的字元個數為1 • 預設的分界字元為EOF(’\0’)
使用範例 #include <iostream> #include <limits> using namespace std; void main() { int x; cin >> x; if (cin.fail()) { cin.clear(); cin.ignore(numeric_limits<streamsize>::max(), '\n'); cout << "Error!" << endl; } else cout << "x = " << x << endl; } cin.ignore用來忽略(捨棄)所有串流輸入的字元。numeric_limits<streamsize>::max()用來算出串流輸入緩衝區的大小,使用前必須引入標頭檔<limits>
執行結果 • 輸入正確 • 輸入錯誤
使用範例二 #include <iostream> #include <limits> using namespace std; void main() { int x; cin >> x; while (cin.fail()) { cin.clear(); cin.ignore(numeric_limits<streamsize>::max(), '\n'); cout << "Error!" << endl; cin >> x; } cout << "x = " << x << endl; } 在本範例示範的例子中,會一直要求使用者輸入,直到輸入正確為止。
注意事項 • 串流擷取運算子(>>)僅會讀取至空白字元為止,即便狀態位元出現異常,串流輸入緩衝區中仍會遺留之前未讀入的字元。 • 可用成員函式ignore()捨棄這些字元。 • 或在狀態回復之後繼續讀取。 • 如要讀入整行文字,或讀取固定個數的字元,可用cin物件中的get()或getline()函式。 • get()與getline()的差別在於:get()不會把換行符號放進去緩衝區,但getline()則會。
Introduction • 本節重點 • 利用fstream物件同時對檔案進行格式化輸入輸出。 • 案例: • 要求使用者輸入一個學號,和其成績後予以儲存;如果輸入重複的學號,則將就資料覆寫。 • 由於資料可能很多筆,因此在此不將資料載入記憶體(例如陣列),而是直接對檔案進行搜尋與存取。 • 學習目標 • 利用狀態位元檢驗檔案物件的狀態 • 利用seekg()/seekp()對檔案指標進行定位
開啟檔案 • 類別fstream所建立的物件可以同時做為讀取或寫入的檔案物件: file.open("test.txt", ios::out | ios::in); • 當模式既為ios::out又為ios::in時,請注意該檔案如果不存在,並不會自動建立該檔案,反而會得到開檔失敗的情況。 • 使用該模式前,請先確認檔案是否存在,如不存在,請自行建立。
成員函式seekg()與seekp() • seekg()是在讀取檔案時,用來移動檔案指標 • 「g」代表get • seekp()是在寫入檔案時,用來移動檔案指標 • 「p」代表put • 在「ios::in | ios::out」模式之下,兩者皆可使用,並請注意,不論檔案的讀取皆會同時移動到檔案指標的位置。
範例實作(Part 1) #include <iostream> #include <fstream> #include <iomanip> using namespace std; void main() { fstream file("test.txt", ios::in); if (!file) { file.clear(); file.open("test.txt", ios::out); } file.close(); file.open("test.txt", ios::out | ios::in); if (!file) { cout << "file could not open!" << endl; return; } … 先以ios::in的模式開啟,檢查檔案是否存在 如果不存在,清除狀態位元(因為failbit被設為true),並改用ios::out開檔(以建立檔案) 重新開啟,便可用 ios::out | ios::in的方式開檔
範例實作(Part 2) do { int option; string id; int data; cout << "1) Insert 2) Output 3) Quit: "; cin >> option; if (option == 3) break; if (option == 1) { string in_ID; int in_data; cin >> in_ID; cin >> in_data; file.clear(); file.seekg(0); while (file >> id >> data) { if (id == in_ID) { //找到重複的ID file.seekp(-16, ios::cur); break; } } if (file.eof()) //遇到EOF,表示資料要寫到最後 file.clear(); //再做一次clear()。Why? file << setw(10) << in_ID << setw(5) << in_data << endl; } 從頭搜尋檔案。 為什麼要先呼叫clear()? 因為如果eofbit被設成true,檔案讀取會失效,所以要先清掉該位元。file.seekg(0);一行用來將檔案指標定位到檔案開頭 用>>作格式化輸入可免去型態轉換的問題,但要注意>>僅讀取至空白字元。 試說明「 file.seekp(-16, ios::cur); 」的意義。 _________________________ _________________________
範例實作(Part 3) … else if (option == 2) //印出所有資料 { file.clear(); file.seekg(0); while (file >> id >> data) { cout << setw(10) << id << setw(5) << data << endl; } } } while (1); file.close(); }
printf()與scanf() • 呼叫printf()與scanf()可分別用來作螢幕輸出與鍵盤輸入。 • 使用前必須引入標頭檔如下: #include <stdio.h> //C 或 #include <cstdio> //C++
螢幕輸出;使用printf() #include <stdio.h> void main() { int x = 2; char *name = “Mary”; double g1 = 3.4; float g2 = 1.7; printf(“%s has %d classes. Her grades are %0.2e and %0.2f.\n”, name, x, g1, g2); } 相較於cout與<<,使用printf()的差異在於,程式設計師必須要根據變數來指定格式字串;例如 整數的格式字串為%d 字串的格式字串為%s 浮點數的格式字串為%f 雙精準浮點數要用%e
鍵盤輸入;使用scanf() #include <stdio.h> void main() { int age; char name[100]; float salary; printf(“Input name: ”); scanf(“%s”, name); printf(“Input age: ”); scanf(“%d”, &age); printf(“Input salary: ”); scanf(“%f”, &salary); printf(“%s is %d years old. His/her salary is %0.2f.\n”, name, age, salary); } 請注意,scanf的第二引數傳入指標;因為該引數的內容傳入函數後會被其修改。 陣列名稱本身及代表一個記憶體位址,因此不用加「&」。
鍵盤輸入;使用scanf() #include <stdio.h> void main() { int age; char name[100]; float salary; printf(“Input name, age and salary: ”); scanf(“%s%d%f”, name, &age, &salary); printf(“%s is %d years old. His/her salary is %0.2f.\n”, name, age, salary); } 可用scanf()一次讀入值到多個變數中,scanf()每次讀取僅讀到空白字元為止。