C++ <fstream> 提供型別安全、RAII 管理的文件 I/O。本文涵蓋文本/二進制模式、格式化輸入輸出、隨機訪問、編碼轉換、大文件流式處理、高效能解析技巧。
核心流類別
| 類別 | 頭文件 | 功能 | 典型用途 |
ofstream | <fstream> | 輸出文件流 | 寫入文本/二進制 |
ifstream | <fstream> | 輸入文件流 | 讀取文本/二進制 |
fstream | <fstream> | 雙向文件流 | 讀寫同一文件 |
ostringstream | <sstream> | 輸出字符串流 | 格式化構建字符串 |
istringstream | <sstream> | 輸入字符串流 | 解析字符串 |
打開模式標誌 (std::ios_base::openmode)
std::ofstream out("data.txt", std::ios::out | std::ios::trunc); // 覆蓋寫入
std::ofstream app("log.txt", std::ios::out | std::ios::app); // 追加
std::ifstream in("data.txt", std::ios::in); // 讀取
std::fstream rw("data.bin", std::ios::in | std::ios::out | std::ios::binary); // 二進制讀寫
std::ofstream ate("data.txt", std::ios::out | std::ios::ate); // 打開定位末尾
// 組合:std::ios::in | std::ios::out | std::ios::binary | std::ios::trunc
文本模式讀寫範例
// 寫入
{
std::ofstream out("config.txt");
out << "server=localhost\nport=8080\ndebug=true\n";
out << std::fixed << std::setprecision(2) << 3.14159 << '\n';
}
// 讀取 (格式化提取)
{
std::ifstream in("config.txt");
std::string key, value;
while (in >> key >> value) { /* 以空白分隔 */ }
// 或逐行
std::string line;
while (std::getline(in, line)) { /* 處理整行 */ }
}
二進制模式讀寫
struct Record { int id; double value; char name[32]; };
// 寫入結構體陣列
{
std::ofstream out("records.bin", std::ios::binary);
Record recs[3] = {{1, 1.5, "A"}, {2, 2.5, "B"}, {3, 3.5, "C"}};
out.write(reinterpret_cast<const char*>(recs), sizeof(recs));
}
// 讀取
{
std::ifstream in("records.bin", std::ios::binary);
Record rec;
while (in.read(reinterpret_cast<char*>(&rec), sizeof(Record))) {
std::cout << rec.id << ' ' << rec.value << ' ' << rec.name << '\n';
}
}
隨機訪問:seekg / seekp / tellg / tellp
std::fstream io("data.bin", std::ios::in | std::ios::out | std::ios::binary);
io.seekg(0, std::ios::end); // 定位末尾
std::streampos size = io.tellg(); // 獲取文件大小
io.seekg(0, std::ios::beg); // 回到開頭
// 讀取第 N 個 Record (0-based)
auto read_record = [&](std::size_t n) {
io.seekg(n * sizeof(Record), std::ios::beg);
Record r; io.read(reinterpret_cast<char*>(&r), sizeof(Record));
return r;
};
// 修改第 N 個 Record
auto write_record = [&](std::size_t n, const Record& r) {
io.seekp(n * sizeof(Record), std::ios::beg);
io.write(reinterpret_cast<const char*>(&r), sizeof(Record));
};
編碼處理:UTF-8 / UTF-16 / 本地編碼
- 原始碼編碼:源文件保存為 UTF-8 (BOM 可選),編譯器
-finput-charset=UTF-8 (GCC) / 默認 UTF-8 (MSVC C++20 /utf-8)。
- 執行期編碼:
std::locale::global(std::locale("")) 設置 C 本地環境,影響 std::cout 輸出編碼。
- 文件編碼轉換:C++20
std::codecvt 已棄用,推薦 iconv、ICU、boost::locale、或手寫 UTF-8 ↔ UTF-16 轉換。
- Windows API:
MultiByteToWideChar (CP_UTF8) / WideCharToMultiByte 配合 std::wifstream / std::wofstream (需 std::codecvt_utf8<wchar_t>,C++11/17/20 均可用但 C++20 棄用)。
大文件高效能處理
- 記憶體映射:
mmap (POSIX) / CreateFileMapping (Windows) / boost::iostreams::mapped_file — OS 頁面換入,隨機訪問極快。
- 流式分塊讀取:固定緩衝區 (64KB-1MB) 循環
read/write,避免一次性載入。
- 非同步 I/O:
io_uring (Linux 5.1+)、IOCP (Windows)、boost::asio::posix::stream_descriptor — 高併發、低延遲。
- 解析器:
simdjson (JSON)、RapidCSV (CSV)、pugixml (XML) — SIMD 加速、單次掃描。
RAII 確保資源釋放
{
std::ofstream out("data.txt"); // 構造打開
out << "data";
} // 析構自動 close() + flush()
// 異常安全
try {
std::ifstream in("data.txt");
if (!in) throw std::runtime_error("open failed");
// ...
} // 即使拋出異常,in 析構仍會 close()
常見錯誤與避坑
| 錯誤 | 後果 | 正確做法 |
未檢查 is_open() / fail() | 靜默失敗、讀取垃圾數據 | 打開後立即檢查,或啟用 exceptions(std::ios::failbit) |
| 文本模式讀二進制文件 | 換行符轉換 (CRLF↔LF) 損壞數據 | 二進制文件必須 std::ios::binary |
while (!in.eof()) 循環 | 最後一行重複處理 | while (in.read(...)) 或 while (std::getline(in, line)) |
混用 >> 和 getline() | 殘留換行符導致空行 | in.ignore(std::numeric_limits<streamsize>::max(), '\n'); 清除 |
| 大對象棧上分配緩衝區 | 棧溢出 | std::vector<char> buf(size) 堆分配 |