一、文件流類概述
C++ 標準庫提供了三個主要的文件流類:
- ifstream (輸入文件流):用于從文件讀取數據
- ofstream (輸出文件流):用于向文件寫入數據
- fstream (文件流):既可讀又可寫
這些類都繼承自 iostream
類,因此可以使用 <<
和 >>
操作符進行格式化I/O操作。
二、文件打開與關閉
1. 打開文件
#include <fstream>
using namespace std;// 方法1:先聲明再打開
ofstream outFile;
outFile.open("output.txt");// 方法2:聲明時直接打開
ifstream inFile("input.txt");// 方法3:使用fstream
fstream ioFile;
ioFile.open("data.txt", ios::in | ios::out);
2. 文件打開模式
模式標志 | 描述 |
---|---|
ios::in | 以讀取方式打開 |
ios::out | 以寫入方式打開 |
ios::app | 追加模式,所有寫入都追加到文件末尾 |
ios::ate | 打開文件后定位到文件末尾 |
ios::trunc | 如果文件存在則清空內容 |
ios::binary | 二進制模式 |
組合示例:
// 以寫入和追加模式打開
ofstream logFile("log.txt", ios::out | ios::app);// 以讀寫和二進制模式打開
fstream dataFile("data.dat", ios::in | ios::out | ios::binary);
3. 關閉文件
outFile.close();
inFile.close();
ioFile.close();
注意:雖然流對象析構時會自動關閉文件,但顯式關閉是個好習慣。
三、文件狀態檢查
ifstream file("data.txt");if (file.is_open()) {// 文件成功打開
} else {// 文件打開失敗
}// 檢查流狀態
if (file.good()) {// 流狀態良好
}if (file.fail()) {// 操作失敗(非致命錯誤)
}if (file.bad()) {// 嚴重錯誤
}if (file.eof()) {// 到達文件末尾
}
四、文本文件操作
1. 寫入文本文件
ofstream out("output.txt");
if (out.is_open()) {out << "第一行文本" << endl;out << 123 << " " << 3.14 << endl;out << "這是另一行文本" << endl;out.close();
} else {cerr << "無法打開文件!" << endl;
}
2. 讀取文本文件
逐行讀取:
ifstream in("input.txt");
string line;
if (in.is_open()) {while (getline(in, line)) {cout << line << endl;}in.close();
}
逐詞讀取:
ifstream in("input.txt");
string word;
if (in.is_open()) {while (in >> word) {cout << word << endl;}in.close();
}
格式化讀取:
ifstream in("data.txt");
int id;
double value;
string name;if (in.is_open()) {while (in >> id >> value >> name) {cout << "ID: " << id << ", Value: " << value << ", Name: " << name << endl;}in.close();
}
五、二進制文件操作
1. 寫入二進制數據
struct Record {int id;char name[50];double salary;
};Record emp = {101, "John Doe", 4500.50};ofstream out("employees.dat", ios::binary);
if (out.is_open()) {out.write(reinterpret_cast<char*>(&emp), sizeof(Record));out.close();
}
2. 讀取二進制數據
Record emp;
ifstream in("employees.dat", ios::binary);
if (in.is_open()) {in.read(reinterpret_cast<char*>(&emp), sizeof(Record));cout << "ID: " << emp.id << endl;cout << "Name: " << emp.name << endl;cout << "Salary: " << emp.salary << endl;in.close();
}
3. 二進制文件隨機訪問
fstream file("data.dat", ios::in | ios::out | ios::binary);// 寫入多個記錄
Record records[3] = {{101, "Alice", 5000},{102, "Bob", 6000},{103, "Charlie", 7000}
};file.write(reinterpret_cast<char*>(records), 3 * sizeof(Record));// 直接讀取第二個記錄
Record emp;
file.seekg(1 * sizeof(Record), ios::beg);
file.read(reinterpret_cast<char*>(&emp), sizeof(Record));// 修改第三個記錄
emp = {103, "David", 7500};
file.seekp(2 * sizeof(Record), ios::beg);
file.write(reinterpret_cast<char*>(&emp), sizeof(Record));file.close();
六、文件定位
1. 獲取當前位置
streampos pos = file.tellg(); // 獲取讀取位置
streampos pos = file.tellp(); // 獲取寫入位置
2. 設置位置
// 絕對定位
file.seekg(0, ios::beg); // 移動到文件開頭
file.seekg(0, ios::end); // 移動到文件末尾// 相對定位
file.seekg(10, ios::cur); // 從當前位置向前移動10字節
file.seekg(-5, ios::cur); // 從當前位置向后移動5字節
七、實用文件操作函數
1. 檢查文件是否存在
#include <sys/stat.h>bool fileExists(const string& filename) {struct stat buffer;return (stat(filename.c_str(), &buffer) == 0);
}
2. 獲取文件大小
streampos getFileSize(const string& filename) {ifstream file(filename, ios::ate | ios::binary);return file.tellg();
}
3. 復制文件
bool copyFile(const string& source, const string& dest) {ifstream src(source, ios::binary);ofstream dst(dest, ios::binary);if (!src || !dst) {return false;}dst << src.rdbuf();return true;
}
4. 讀取CSV文件
void readCSV(const string& filename) {ifstream file(filename);string line;while (getline(file, line)) {stringstream ss(line);string cell;vector<string> row;while (getline(ss, cell, ',')) {row.push_back(cell);}// 處理行數據for (const auto& val : row) {cout << val << "\t";}cout << endl;}
}
八、錯誤處理最佳實踐
void processFile(const string& filename) {ifstream file(filename);if (!file) {cerr << "錯誤:無法打開文件 " << filename << endl;perror("原因"); // 打印系統錯誤信息return;}try {// 文件處理代碼string line;while (getline(file, line)) {// 處理每一行}if (file.bad()) {throw runtime_error("讀取文件時發生嚴重錯誤");}if (file.fail() && !file.eof()) {throw runtime_error("文件格式錯誤");}} catch (const exception& e) {cerr << "錯誤: " << e.what() << endl;file.close();return;}file.close();
}
九、性能考慮
-
緩沖區大小:默認緩沖區大小可能不適合大文件操作,可以自定義:
char buffer[8192]; // 8KB緩沖區 ifstream file; file.rdbuf()->pubsetbuf(buffer, sizeof(buffer)); file.open("largefile.dat");
-
二進制 vs 文本:二進制操作通常比文本操作更快,特別是對于大量數據。
-
內存映射文件:對于極大文件,考慮使用操作系統特定的內存映射文件API。
十、C++17 文件系統庫
C++17 引入了 <filesystem>
庫,提供了更高級的文件操作:
#include <filesystem>
namespace fs = std::filesystem;// 檢查文件是否存在
if (fs::exists("test.txt")) {// 獲取文件大小auto size = fs::file_size("test.txt");// 復制文件fs::copy("source.txt", "destination.txt");// 刪除文件fs::remove("old_file.txt");// 遍歷目錄for (auto& entry : fs::directory_iterator(".")) {cout << entry.path() << endl;}
}
通過掌握這些文件操作技術,您可以在C++程序中高效地處理各種文件I/O任務。