1 / 69

Chap 12 資料流與檔案的存取

Chap 12 資料流與檔案的存取. 如果程式所處理的資料只能寫在原始程式內部,或以互動的方式由鍵盤逐一輸入,則功能將很有限。本章探討如何從檔案讀取資料,以及將處理後的資料存入檔案的方法。. 資料流與檔案的存取. 12.1  資料流 12.2  檔案的存取 12.3  檔案的存取模式 12.4  資料的讀取與寫入 12.5  檔案內容的位置標記 12.6  將檔案的存取寫成函數. 資料流 (stream). 回顧標準的輸出入管道 cout 和 cin : 範例程式 檔案 BasicIO.cpp // BasicIO.cpp

Download Presentation

Chap 12 資料流與檔案的存取

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Chap 12 資料流與檔案的存取 如果程式所處理的資料只能寫在原始程式內部,或以互動的方式由鍵盤逐一輸入,則功能將很有限。本章探討如何從檔案讀取資料,以及將處理後的資料存入檔案的方法。

  2. 資料流與檔案的存取 • 12.1 資料流 • 12.2 檔案的存取 • 12.3 檔案的存取模式 • 12.4 資料的讀取與寫入 • 12.5 檔案內容的位置標記 • 12.6 將檔案的存取寫成函數

  3. 資料流 (stream) • 回顧標準的輸出入管道cout和cin: • 範例程式 檔案 BasicIO.cpp // BasicIO.cpp #include <iostream> using namespace std; int main () { int N; cout << “請輸入一個整數:” << endl; cin >> N; cout << “您輸入的是 “ << N << endl; return 0; }

  4. 操作結果 請輸入一個整數: 4563 您輸入的是 4563

  5. 資料流 (stream)和資料流物件(stream object)

  6. 物件 (object) 和類別 (class) • 物件(object),其原文有「個體」、「對象」等多種意涵。 • 類別是一種廣義的資料型態,它可以由程式寫作者自行定義。 • 由類別定義的「物件」不僅包括資料,而且還包括與物件進行互動所需要的各種函數。 • cin和cout分別屬於istream和ostream兩個類別。我們不需要定義cin和cout,因為它們是最常用的預設物件。

  7. 驅動程式 • 驅動程式 (drivers) 是作業系統 (例如Windows和Linux) 的一部份,用來與硬體 (譬如鍵盤和顯示器) 溝通,以處理硬體裝置與記憶體之間的資料流動。如下圖所示: 資料流 (stream) 與硬體之間的關係圖

  8. 使用成員函數 (member functions) 的語法 • 類別(Class) 內部所定義的各種函數稱為成員函數(member functions) 或方法(methods)。 • 敘述 cin.get(Ch); 裏,句點「.」稱為成員運算符號(member operator),它的前面是物件的名稱,而其後是成員函數的名稱。

  9. 呼叫物件cin的成員函數get()

  10. 檔案 • 在一個名稱之下,內含有資料的獨立儲存單位。檔案通常儲存於硬碟、磁片、光碟、磁帶或 MO 等媒體上。

  11. 檔案的存取 • 程式開頭要加入標頭檔: <fstream>,以含入必要的類別宣告。 • 必需明確地宣告和定義讀寫檔案所需的管道 (亦即資料流)。例如: 要讀取檔案時,定義類別為ifstream 的輸入型檔案資料流: ifstream FileInput; 要寫入檔案時,則需定義類別為ofstream的輸出型檔案資料流: ofstream FileOutput; 讀寫檔案前,需要先執行將檔案「開啟」 (open) 的動作。讀寫完畢後,最好執行將檔案「關閉」(close) 的動作。

  12. 檔案和程式、資料流之間的關係

  13. 資料存取的語法 • 一旦檔案被開啟,則資料存取的語法和先前cin、cout的用法相同。例如: FileInput >> x; FileOutput << x << endl • 程式內部所直接使用的是各個「已經聯結到特定檔案的檔案資料流」,而不再提及檔案的名稱。

  14. 將資料流聯結到檔案的語法 • 例如,在檔案開啟時將敘述寫成: FileInput.open(“FileA.txt”); • 檔案關閉時,則不用再寫出檔案名稱,只需寫成: FileInput.close();

  15. 範例程式TaxFile.cpp • 從檔案Income.txt將收入的數值讀到變數Income裏,再呼叫函數CalculateTax() 以計算稅額Tax,最後把稅額的數值輸出到顯示器,並同時存到檔案Tax.txt裏。 • 示範了如何宣告資料流,如何開啟和關閉檔案,以及如何在檔案裏存取資料的細節。

  16. 範例程式 檔案 TaxFile.cpp // TaxFile.cpp #include <iostream> #include <fstream> using namespace std; double CalculateTax(double); // 函數的宣告 // ---主程式------------------------ int main() { char* FileNameIn = "Income.txt"; // 以字串的方式宣告檔名 char* FileNameOut = "Tax.txt"; double Income, Tax; ifstreamFileInput; ofstreamFileOutput; FileInput.open ( FileNameIn ); FileOutput.open( FileNameOut );

  17. if (!FileInput) { cout << "檔案: " << FileNameIn << " 開啟失敗!" << endl; exit(1); } if (!FileOutput) { cout << "檔案: " << FileNameOut << " 存檔失敗!" << endl; exit(1); } FileInput >> Income; Tax = CalculateTax(Income); cout << "您要繳的綜合所得稅是: " << Tax << " 元";

  18. FileOutput << "您要繳的綜合所得稅是: " << Tax << " 元"; FileInput.close(); // 關閉輸入檔案 FileOutput.close(); // 關閉輸出檔案 return 0; } // ---函數 CalculateTax() 的定義----- // 改寫自4.4節「巢狀if-else敘述」 // 程式 Tax.cpp 以計算稅額 double CalculateTax(double GIncome) { double Tax; if (GIncome < 0.0) cout << "您輸入的綜合所得淨額沒有意義!\n"; else if (GIncome < 330000.0) Tax = GIncome * 0.06;

  19. else if (GIncome < 890000.0) Tax = GIncome * 0.13 - 23100; else if (GIncome < 1780000.0) Tax = GIncome * 0.21 - 94300; else if (GIncome < 3340000.0) Tax = GIncome * 0.3 - 254500; else Tax = GIncome * 0.4 - 588500; return Tax; }

  20. 操作結果 您要繳的綜合所得稅是:50715.2元

  21. 檔案的存取模式 1. create (創造) 和replace (取代): 如果檔案不存在,要不要產生一個新的檔案,如果檔案已經存在,要不要將其取代。 2. append (附加) 和 replace (取代): 從資料流輸入的資料要接在原來的內容之後,還是要取代原來的內容。

  22. 另外一種檢查開檔動作有沒有失敗的語法 • 將開檔部份的敘述改寫如下: FileInput.open (FileNameIn, ios::nocreate); if (FileInput.fail()) { cout << "檔案: " << FileNameIn << " 開啟失敗!" << endl; exit(1); }

  23. 結合資料流的宣告敘述和開檔的動作 • 如果檔案資料流FileInput在程式中只跟一個檔案聯結,則可以進一步寫成: ifstream FileInput (FileName, ios::nocreate); • 參數 nocreate 又稱為模式指示參數(mode indicator)。

  24. 常用的模式指示參數(mode indicator)

  25. 統一使用fstream類別其後區別該資料流的方向 fstream FileInput; fstream FileOutPut; FileInput.open(FileName, ios::in); FileOutput.open(FileName, ios::out);

  26. 結合模式指示參數 • 進一步決定「如果要開啟以供輸入的檔案不存在」的處理方式,使用「或」 (也就是or) 的語法: FileInput.open(FileName, ios::in | ios::nocreate);

  27. EOF (end-of-file) • 檔案的結束記號。 • EOF為標頭檔 <fstream> 內定義的常數。 • 它在Windows和DOS作業系統都是使用第26個ASCII字元‘Ctrl-Z’,在某些作業系統內EOF的值設為 –1。

  28. 範例程式ConvFile.cpp • 使用本節介紹的語法進行讀檔與存檔的操作。首先從檔案Original.txt 逐一讀進字母,再經 <cctype> 中的函數toupper()將字母轉換為大寫字母後,逐一存進檔案Converted.txt 中。

  29. 範例程式 檔案 ConvFile.cpp // ConvFile.cpp #include <iomanip> #include <iostream> #include <fstream> #include <cctype> using namespace std; char* FileNameIn = "Original.txt"; char* FileNameOut = "Converted.txt"; // ---函數 Sort() 的宣告---- int Sort(char X); // ---主程式------------------------ int main() {

  30. char C; fstream FileInput(FileNameIn, ios::in); if (!FileInput) { cout << "檔案: " << FileNameIn << " 開啟失敗!" << endl; exit(1); } fstream FileOutput(FileNameOut, ios::out); if (!FileOutput) { cout << "檔案: << FileNameOut << " 存檔失敗!" << endl; exit(1); }

  31. while ((C = FileInput.get())!= EOF) { switch (Sort(C)) { case 1: FileOutput << char(toupper(C)); break; case 0: case 2: case 3: case 4: FileOutput << C ; break; case 5: FileOutput << "Other" << endl; break; default: cout << "程式有問題!" << endl; } }

  32. FileOutput.close(); FileInput.close(); cout << "成功存於檔案 " << FileNameOut << " 內.\n"; return 0; } // ---函數 Sort() 的定義--------- int Sort(char X) { if (isupper(X)) return 0; else if (islower(X)) return 1;

  33. else if (isdigit(X)) return 2; else if (isspace(X)) return 3; else if (ispunct(X)) return 4; else return 5; }

  34. 操作結果 (在顯示器上會看到下列訊息:) 成功存於檔案 Conerted.txt 內. • 檔案Original.txt 的內容是:I love you, certainly. • 檔案Converted.txt 的內容是:I LOVE YOU, CERTAINLY.

  35. 檔案資料流 fstream 的成員函數 表12.4fstream的成員函數

  36. 範例程式RWFiles.cpp • 從檔案 Record.txt 將字串資料和數值資料分別讀到字串陣列Name 和數值陣列Score裏,再單獨對Score進行計算,最後把字串和數值存到檔案 Saved.txt 裏。 • 這個程式示範輸入資料流的成員函數peek()的使用細節,它用來檢查即將讀入的字元是否為 EOF。

  37. 範例程式 檔案 RWFiles.cpp // RWFiles.cpp #include <iostream> #include <iomanip> #include <fstream> using namespace std; char* FileNameIn = "Record.txt"; char* FileNameOut = "Saved.txt"; // ---主程式------------------------ int main() { const int MaxNum = 40; const int MaxSize = 20; charName[MaxNum][MaxSize]; int Score[MaxNum];

  38. fstreamFileInput(FileNameIn, ios::in); if (!FileInput) { cout << "檔案: " << FileNameIn << " 開啟失敗!" << endl; exit(1);} fstream FileOutput(FileNameOut, ios::out); if (!FileOutput) { cout << "檔案: " << FileNameOut << " 存檔失敗!" << endl; exit(1);} int Count=0; while (FileInput.peek()!= EOF && (Count < MaxNum)) { FileInput >> Name[Count] >> Score[Count]; Count++; }

  39. for (int i=0; i< Count; i++) { Score[i] = Score[i]*0.8+20; FileOutput << '(' << i+1 << ')' << setw(12) << Name[i] << " " << setw(5) << Score[i] << endl; } FileOutput.close(); FileInput.close(); cout << "成功存於檔案 " << FileNameOut << " 內." << endl; return 0; }

  40. 操作結果 (在顯示器上會看到下列訊息:) 成功存於檔案Saved.txt 內. • 檔案Record.txt 的內容是: 王能治 87 李大為 92 蕭飛雪 78 張心怡 86 • 檔案Saved.txt 的內容是: (1) 王能治 89 (2) 李大為 93 (3) 蕭飛雪 82 (4) 張心怡 88

  41. 檔案內容的位置標記(File Position Marker) • 位置標記使用「相對位置」的觀念:從某個位置當起點,再加上偏移量(offset) 以到達所要的位置。 • 按照起點位置的不同,位置標記在使用上有下列三種模式 (modes of the file position marker): • ios::beg從檔案的開頭算起 (beg為begin的縮寫) • ios::cur從目前位置算起 (cur為current的縮寫) • ios::end從檔案結尾算起 (end為結尾的意思)

  42. 配合檔案內容的位置標記的成員函數 表12.5.1 配合檔案內容的位置標記的成員函數 • 「位置標記」以及「偏移量」的資料型態必需是長整數(long int)。 • 各成員函數名稱中的g是get (取得),而p是put (放入) 的簡寫。

  43. 位置標記的成員函數舉例 1. FileOutput.seekp(5L, ios::cur);:將位置標記在輸出檔案中從目前的位置往前移動5個字元的距離。 2. FileOutput.seekp(2L, ios::beg);:將位置標記在輸出檔案中移到第3個字元的位置。 3. FileOutput.seekp(-5L, ios::end);:將位置標記在輸出檔案中移到倒數第5個字元的位置。 4. FileInput.seekg(0L, ios::beg);:將位置標記移到輸入檔案開頭的位置。 5. FileInput.seekg(0L, ios::end);:將位置標記移到輸入檔案結尾的位置。

  44. 使用成員函數tellg()從檔案資料流中獲得已開啟檔案中的字元數目使用成員函數tellg()從檔案資料流中獲得已開啟檔案中的字元數目 • 例如: ifstream FileInput (“Data.txt”);// 開啟檔案 Data.txt long CharNum; FileInput.seekg(0L, ios::end);// 移到檔案結束的地方 CharNum = FileInput.tellg();// 獲得字元數目 就可以得到 Data.txt 中的字元數目,並將它儲存在長整數CharNum中。

  45. 使用seekg() 以移動位置標記在已開啟的檔案中游走 • 把位置標記先移到檔案結尾處,再逐一往前移動,把既有檔案中的字元次序顛倒過來: long Offset; char Ch; for (Offset=1L; Offset<= CharNum; Offset++) { FileInput.seekg(-Offset, ios::end); Ch = FileInput.get(); FileOutput << Ch; }

  46. 範例程式FileReverse.cpp • 我們將上述倒轉檔案內容的指令寫成完整的範例程式。 • 這個程式首先從檔案Original.txt 讀取資料,將檔案中的字元次序顛倒過來後,逐一存於檔案Reversed.txt中。

  47. 範例程式 檔案 FileReverse.cpp // FileReverse.cpp #include <iostream> #include <iomanip> #include <fstream> using namespace std; char* FileNameIn = "Original.txt"; char* FileNameOut = "Reversed.txt"; // ---主程式------------------------ int main() { long CharNum, Offset; char Ch; fstream FileInput(FileNameIn, ios::in);

  48. if (!FileInput) { cout << “檔案“ << FileNameIn<< " 開啟失敗!" << endl; exit(1); } fstream FileOutput(FileNameOut, ios::out); if (!FileOutput) { cout << “檔案” << FileNameOut<< " 存檔失敗!" << endl; exit(1); } cout << “檔案” << FileNameIn<< " 開啟成功" << endl; FileInput.seekg(0L, ios::end); // 移到檔案結束的地方

  49. CharNum = FileInput.tellg(); // 獲得字元數目 for (Offset=1L; Offset<= CharNum; Offset++) { FileInput.seekg(-Offset, ios::end); Ch = FileInput.get(); FileOutput << Ch; } FileOutput.close(); // 關閉FileOutput FileInput.close(); // 關閉FileInput cout << "倒轉檔案 " << FileNameIn << " 後的內容\n成功存於檔案 " << FileNameOut << " 內." << endl; return 0; }

  50. 範例程式FileAccess.cpp • WriteString():使用檔案以儲存字串的函數。 • WriteData():使用檔案以儲存數字的函數。 • 以字串為參數傳遞檔案名稱,參數Mode則是模式指示參數 (mode indicator),當Mode為1時是append (附加),Mode為0時是replace (取代)。

More Related