【C++ 筆記】檔案處理 - part 27 === 目錄(Table of Contents): [TOC] --- 很感謝你點進來這篇文章。 你好,我並不是什麼 C++、程式語言的專家,所以本文若有些錯誤麻煩請各位鞭大力一點,我極需各位的指正及指導!!本系列文章的性質主要以詼諧的口吻,一派輕鬆的態度自學程式語言,如果你喜歡,麻煩留言說聲文章讚讚吧! 至此之前,我們已經有學過 `<iostream>` 的標準輸入輸出流 cin, cout。 那本篇要學的就是 `<fstream>` 裡面的三種流,`ofstream`, `ifstream`, `fstream`。 基本檔案處理(File Handing) --- `<fstream>` 定義了三種資料型態: 表格參考:[菜鳥教程](https://www.runoob.com/cplusplus/cpp-files-streams.html) | 資料型態 | 敘述 | | -------- | -------- | | ofstream | (o:Output)輸出檔案流,用於建立檔案並向檔案寫入訊息。 | | ifstream | (i:Input)輸入檔案流,用於從檔案中讀取訊息。| | fstream | 上述兩種資料型態的結合體,亦即同時具備兩種資料型態的功能。 | 要做到檔案處理,需引入 `<iostream>` 跟 `<fstream>`。 ### 檔案處理的基本操作 主要分成: - 開啟檔案 - 讀寫操作 - 關閉檔案 ### 開啟檔案 語法: ```cpp fstream str("filename.ext", mode); ``` - str:給這個流的名字。 - filename:檔名。 - mode:表示要與這個檔案互動的方式。 ### 檔案開啟模式(mode) Table Source:https://www.geeksforgeeks.org/cpp/file-handling-c-classes/ | 模式(Mode) | 說明(Description) | | ------------- | --------------------------- | | `ios::in` | 以讀取模式開啟檔案。若檔案不存在,將無法開啟。 | | `ios::out` | 以寫入模式開啟檔案:內部的串流緩衝區支援輸出操作。 | | `ios::binary` | 以二進位模式進行操作,而非文字模式。 | | `ios::ate` | 輸出的起始位置會設在檔案結尾。 | | `ios::app` | 所有輸出操作都會發生在檔案末端,並附加於現有內容之後。 | | `ios::trunc` | 開啟檔案時會清除其原有內容(若存在)。 | 這些模式可透過 OR 運算子 `|` 同時做到兩種操作: `fstream str("file.txt", ios::in | ios::out)` --- `ios::in` 範例: 將 example.txt 與 eg.cpp 放到同目錄下。 ![image](https://hackmd.io/_uploads/BksSEfaEex.png) ![image](https://hackmd.io/_uploads/SkZINM6Ell.png) ```cpp= // eg.cpp #include <iostream> #include <fstream> using namespace std; int main(){ fstream file("example.txt", ios::in); if (file.is_open()){ // is_open() 回傳 bool 檢查檔案是否成功開啟 string line; getline(file, line); cout << "example.txt 的內容是:" << line << endl; file.close(); // 關閉檔案 } else{ cout << "檔案可能不存在" << endl; } return 0; } ``` 使用 Code::Blocks 的輸出結果為: ![image](https://hackmd.io/_uploads/SkROEMTNgx.png) --- `ios::out` 寫入檔案(會清空原本的內容): 原本 example.txt 寫的是 `Big Handsome Boy`,透過以下程式碼後,會覆蓋掉原本的字。 ```cpp= #include <iostream> #include <fstream> using namespace std; int main() { fstream file("example.txt", ios::out); file << "這是新資料。" << endl; file.close(); return 0; } ``` 執行後: ![image](https://hackmd.io/_uploads/B1rJrG6Nel.png) --- `ios::ate` 開啟檔案後會跳到檔案尾: ```cpp= #include <iostream> #include <fstream> using namespace std; int main() { fstream file("example.txt", ios::in | ios::out | ios::ate); if (file.is_open()) { file << "附加內容(位置不強制)" << endl; file.close(); } return 0; } ``` 此時檔案的寫入就會從檔案結尾開始: ![image](https://hackmd.io/_uploads/HyeRQX64ge.png) --- `ios::app` 強制所有寫入附加到檔案結尾: ```cpp= #include <iostream> #include <fstream> using namespace std; int main() { fstream file("example.txt", ios::app); file << "這行會附加在檔案末尾。" << endl; file.close(); return 0; } ``` 執行後: ![image](https://hackmd.io/_uploads/H1rmEXTVle.png) --- `ios::trunc` 清空檔案: `ios::out` 本身預設就帶有 `ios::trunc`,若加上 `ios::app` 則不會清空。 ```cpp= #include <iostream> #include <fstream> using namespace std; int main() { ofstream file("example.txt", ios::out | ios::trunc); file << "檔案原有內容被清除。" << endl; file.close(); return 0; } ``` ### 讀取檔案 1. 透過自定義的 fstream 名稱加上 `>>` 運算子可讀取檔案內的內容給變數。 example.txt: ![image](https://hackmd.io/_uploads/H1538X6Nlx.png) ```cpp= #include <bits/stdc++.h> using namespace std; int main(){ fstream file("example.txt"); string s; file >> s; cout << "讀取到的是 : " << s; return 0; } ``` Output: ![image](https://hackmd.io/_uploads/Sy_pIQaEex.png) 這種方式就如同用 cin 一樣,一旦讀到含有空格的字串,那就只會讀到第一個字串。 2. 用 `getline(file, s)` 去讀。 ```cpp= #include <bits/stdc++.h> using namespace std; int main(){ fstream file("example.txt"); string s; getline(file, s); cout << "讀取到的是 : " << s; return 0; } ``` Output: ![image](https://hackmd.io/_uploads/H1WQwXaElx.png) ### 關閉檔案 當檔案處理完畢後,很重要的一件事是:務必要關上檔案。 語法: `file.close()` 為什麼呢?除了長時間開啟檔案,一直在那占用資源以外,關閉檔案也能以避免記憶體洩漏、資料遺失等問題。 檔案處理上的錯誤 --- 為什麼要知道這些錯誤?主要是防範於未然,有時候像是忘記要讀取,那這時候可以加上一個自定義的讀取錯誤來提醒自己要加上讀取。 ### 檔案開啟錯誤 用 `is_open()` 函式去判斷是否能開啟檔案。 如下範例(Source from [GeeksForGeeks](https://www.geeksforgeeks.org/cpp/file-handling-c-classes/)): ```cpp= #include <bits/stdc++.h> using namespace std; int main() { fstream file("nonexistent_file.txt", ios::in); // Check if the file is opened if (!file.is_open()) { cerr << "Error: Unable to open file!" << endl; return 1; } file.close(); return 0; } ``` 如果目錄下沒有 noneexistent_file.txt 文件的話,就會直接輸出這行: ``` Error: Unable to open file! ``` 註:cerr 被稱為標準錯誤輸出,與原本的 cout 相比,他沒有緩衝就直接輸出了,如其名,主要用於輸出錯誤、警告等訊息。 ### 讀寫失敗 `if (!getline(file, s))` 判斷是否有資料讀取。 稍微改一下的範例(Source from [GeeksForGeeks](https://www.geeksforgeeks.org/cpp/file-handling-c-classes/)): 如下程式碼中,只有給 `ios::out`,而沒有 `ios::in`,也不做任何讀取的動作,那當然就會出錯囉。 ```cpp= #include <bits/stdc++.h> using namespace std; int main(){ fstream file("example.txt", ios::out); if (!file.is_open()) { cerr << "Error: Unable to open file!" << endl; return 1; } string s; if (!getline(file, s)){ cerr << "Error: Failed to read data." << endl; } file.close(); return 0; } ``` ### 檔案結尾(EOF)錯誤 臭名昭著的 EOF,全名為 End of file,競程上時常碰到的對手。 可用 `eof()` 函式判斷是否有到 EOF。 範例(Source from [GeeksForGeeks](https://www.geeksforgeeks.org/cpp/file-handling-c-classes/)): ```cpp= #include <bits/stdc++.h> using namespace std; int main() { ifstream file("GFG.txt"); if (!file.is_open()) { cerr << "Error: Unable to open file!" << endl; return 1; } string line; while (getline(file, line)) cout << line << endl; // Check for eof if (file.eof()) cout << "Reached end of file." << endl; else cerr << "Error: File reading failed!" << endl; file.close(); return 0; } ``` 總結 --- ### 基本檔案處理(File Handling) 需包含標頭檔 `<iostream>` 和 `<fstream>`。 `<fstream>` 定義三種資料型態: - ofstream:輸出檔案流,用於建立檔案並寫入資料。 - ifstream:輸入檔案流,用於從檔案讀取資料。 - fstream:結合輸入與輸出功能的檔案流。 ### 檔案開啟與模式 開啟檔案語法: ```cpp fstream file("filename.ext", mode); ``` 常用模式(可用 | 組合): * ios::in:讀取模式,檔案不存在則開啟失敗。 * ios::out:寫入模式,會清空檔案內容(除非搭配 ios::app)。 * ios::binary:二進位模式。 * ios::ate:開啟後指標移到檔案尾,可從尾端開始寫入。 * ios::app:所有寫入附加在檔案末尾。 * ios::trunc:開啟時清空檔案內容(ios::out 預設帶此模式)。 ### 讀寫操作 - 讀取: 1. 使用 >> 運算子讀取資料(遇空白停止)。 2. 使用 `getline(file, string_var)` 讀取整行文字。 - 寫入: - 使用 << 運算子寫入資料。 ### 關閉檔案 使用 `file.close()` 關閉檔案,釋放資源,避免資料遺失或記憶體洩漏。 ### 錯誤處理 - 開啟檔案失敗可用 `is_open()` 判斷並提示錯誤。 - 讀取失敗可用 `if (!getline(file, s))` 判斷。 - 使用 `eof()` 判斷是否到達檔案結尾,避免讀取過頭。 參考資料 --- [C++中cout和cerr的区别?_cerr<<-CSDN博客](https://blog.csdn.net/Garfield2005/article/details/7639833) [C++ cerr object | W3Schools](https://www.w3schools.com/cpp/ref_iostream_cerr.asp) [File Handling through C++ Classes - GeeksforGeeks](https://www.geeksforgeeks.org/cpp/file-handling-c-classes/) [C++ 文件和流 | 菜鸟教程](https://www.runoob.com/cplusplus/cpp-files-streams.html)