C++ 中的文件和流是程序與外部數據交互的橋樑,它們提供了一套強大而靈活的標準庫工具,用於處理數據的持久化存儲與讀取。下面這張表格彙總了其核心組件和用途,幫你快速建立整體認知。
|
組件類別
|
核心類/頭文件
|
主要用途
|
|
文件流 (File Streams) |
|
用於對磁盤文件進行讀取、寫入和讀寫操作。 |
|
字符串流 (String Streams) |
|
用於像操作流一樣處理字符串,常用於數據格式轉換或拼接。 |
|
標準 I/O 對象 |
|
用於處理標準輸入(如鍵盤)和標準輸出/錯誤(如控制枱)。 |
|
文件系統庫 (C++17) |
|
提供跨平台的文件和目錄管理操作,如路徑處理、創建、刪除、遍歷。 |
🔑 核心文件流操作
在C++中進行文件I/O,主要依賴定義在 <fstream>頭文件中的三個類:
std::ifstream:輸入文件流,專門用於從文件讀取數據。std::ofstream:輸出文件流,專門用於向文件寫入數據。std::fstream:兼具輸入和輸出功能的文件流。
文件操作通常遵循以下標準步驟:
- 包含頭文件:
#include <fstream>。 - 創建流對象並打開文件:可以在構造函數中直接指定文件名和模式,也可以先創建對象,再調用
open()方法。 - 檢查是否打開成功:使用
is_open()方法或直接判斷流對象的狀態(如if (!file))是至關重要的好習慣。 - 進行讀寫操作。
- 關閉文件:雖然流對象析構時會自動關閉文件,但顯式調用
close()可以及時釋放資源。
寫入文件
使用 ofstream寫入文本數據非常簡單,其用法和 cout類似:
#include <iostream>
#include <fstream>
int main() {
std::ofstream outFile("example.txt"); // 創建並嘗試打開文件
if (!outFile) { // 檢查文件是否成功打開
std::cerr << "Failed to open file for writing!" << std::endl;
return 1;
}
outFile << "Hello, World!" << std::endl;
outFile << "This is a second line." << std::endl;
outFile.close(); // 關閉文件
std::cout << "Data written successfully." << std::endl;
return 0;
}
讀取文件
使用 ifstream讀取文件有幾種常見方式:
- 逐行讀取:使用
std::getline(),適合處理文本文件。 - 逐詞讀取:使用
>>操作符,它會忽略空白字符。 - 一次性讀取整個文件:結合
std::istreambuf_iterator,適用於已知文件不大的情況。
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ifstream inFile("example.txt");
if (!inFile.is_open()) {
std::cerr << "Failed to open file for reading!" << std::endl;
return 1;
}
std::string line;
while (std::getline(inFile, line)) { // 逐行讀取
std::cout << line << std::endl;
}
inFile.close();
return 0;
}
⚙️ 掌握關鍵技術與模式
靈活運用文件操作,需要理解一些關鍵概念。
文件打開模式
在打開文件時,可以指定模式來控制其行為,模式通過位或操作符 |組合:
|
模式
|
描述
|
|
|
為讀取打開文件。 |
|
|
為寫入打開文件(默認會截斷已有文件)。 |
|
|
追加模式,所有寫入都添加到文件尾部。 |
|
|
以二進制模式打開文件,避免某些轉換。 |
|
|
如果文件已存在,則先清空其內容( |
例如,以讀寫和追加模式打開文件:std::fstream file("data.txt", std::ios::in | std::ios::out | std::ios::app);。
二進制文件操作
處理非文本數據(如圖片、結構體)時,需使用二進制模式。這會禁止換行符轉換等特殊處理,保證數據原樣讀寫。
- 寫入:使用
write()成員函數,它接受一個char*指針和要寫入的字節數。通常需要reinterpret_cast進行類型轉換。 - 讀取:使用對應的
read()成員函數。
#include <fstream>
struct Data {
int id;
double value;
};
int main() {
Data data = {1, 3.14};
// 寫入二進制文件
std::ofstream out("data.bin", std::ios::binary);
out.write(reinterpret_cast<char*>(&data), sizeof(data));
out.close();
// 讀取二進制文件
Data readData;
std::ifstream in("data.bin", std::ios::binary);
in.read(reinterpret_cast<char*>(&readData), sizeof(readData));
in.close();
std::cout << "ID: " << readData.id << ", Value: " << readData.value << std::endl;
return 0;
}
隨機訪問文件指針
C++允許在文件內任意位置進行讀寫,即隨機訪問。這是通過操作文件指針實現的:
seekg(offset, origin):移動輸入(讀)指針。origin可以是std::ios::beg(文件頭)、std::ios::cur(當前位置)或std::ios::end(文件尾)。seekp(offset, origin):移動輸出(寫)指針。tellg()/tellp():返回當前讀/寫指針的位置。
例如,file.seekg(-10, std::ios::end);將讀指針移動到距離文件末尾10個字節的位置。
🛡️ 錯誤處理與狀態檢查
文件操作容易出錯,健壯的程序必須檢查操作結果。文件流對象提供了一系列方法來檢查其狀態:
good():流處於正常狀態,所有操作都可用。eof():已到達文件末尾。注意,僅當嘗試讀取超過文件尾時,此標誌才被設置。fail():上次操作失敗(如類型不匹配),但流尚未完全損壞。bad():發生了嚴重錯誤,流可能無法再使用。
通常,在文件操作後檢查流狀態更可靠,例如 while (getline(inFile, line)) { ... }會在讀取失敗(包括到達文件尾)時自動退出循環。
🚀 進階主題與現代方法
字符串流
<sstream>頭文件提供了字符串流類(如 std::istringstream, std::ostringstream),它們允許將字符串當作流來處理,常用於數據格式轉換或拼接。
#include <sstream>
#include <iostream>
#include <string>
int main() {
// 從字符串解析數據
std::string input = "100 3.14 hello";
std::istringstream iss(input);
int i; float f; std::string s;
iss >> i >> f >> s;
// 使用字符串流拼接
std::ostringstream oss;
oss << "Parsed: " << i << ", " << f << ", " << s;
std::cout << oss.str() << std::endl; // 輸出: Parsed: 100, 3.14, hello
return 0;
}
C++17 文件系統庫
C++17 引入了 <filesystem>庫,它提供了一套現代化、跨平台的文件和目錄操作工具,大大簡化了路徑處理、目錄遍歷等任務。
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main() {
fs::path p = "example.txt";
if (fs::exists(p)) {
std::cout << "File size: " << fs::file_size(p) << " bytes\n";
}
// 遍歷目錄
for (const auto& entry : fs::directory_iterator(".")) {
std::cout << entry.path() << std::endl;
}
return 0;
}
💎 總結
C++的文件和流機制構成了一個強大而靈活的數據I/O生態系統。通過掌握基於 <fstream>的傳統文件操作、理解二進制處理和隨機訪問、善用 <sstream>進行字符串處理,並在支持C++17及以上標準的環境中積極運用現代化的 <filesystem>庫,你能夠高效地處理各種數據持久化需求。
希望這份介紹能幫助你深入理解並有效運用C++的文件和流。如果你對某個特定方面還有疑問,我們可以繼續深入探討。