以下內容將從文件流類體系、打開模式、文本與二進制 I/O、隨機訪問、錯誤處理、性能優化等方面,詳解 C++ 中文件輸入輸出的使用要點,并配以示例。
一、文件流類體系
C++ 標準庫提供三種文件流類型,均定義在 <fstream>
中:
std::ifstream
:輸入文件流,用于從文件讀取(繼承自std::istream
)。std::ofstream
:輸出文件流,用于向文件寫入(繼承自std::ostream
)。std::fstream
:讀寫文件流,可同時做輸入和輸出(繼承自兩者)。
它們的典型用法與標準流 (cin
/cout
) 類似,但需要先打開文件。
二、打開模式(std::ios_base::openmode
)
打開時可指定下列模式的按位或(|
)組合:
模式 | 含義 |
---|---|
std::ios::in | 打開用于讀操作 |
std::ios::out | 打開用于寫操作 |
std::ios::app | 所有寫操作均追加到文件末尾 |
std::ios::trunc | 打開時清空已有內容(默認對 ofstream 生效) |
std::ios::binary | 二進制模式(屏蔽文本模式下的換行轉換) |
std::ios::ate | 打開后立即定位到文件末尾 |
示例:
std::ifstream fin("data.txt", std::ios::in);
std::ofstream fout("out.txt", std::ios::out | std::ios::trunc);
std::fstream fs("db.bin", std::ios::in|std::ios::out|std::ios::binary);
三、文本文件 I/O
1. 寫入文本
-
operator<<
與格式化std::ofstream fout("log.txt"); fout << "Count = " << count << '\n';
-
std::endl
與'\n'
std::endl
會插入換行并刷新緩沖,頻繁使用有性能開銷;推薦輸出'\n'
。
2. 讀取文本
-
按詞/按行讀取
std::ifstream fin("data.txt"); std::string word; while (fin >> word) { /* 按空白分隔 */ }fin.clear(); fin.seekg(0); std::string line; while (std::getline(fin, line)) { /* 讀取整行,不含換行符 */ }
-
混合
>>
與getline
使用>>
后會留下換行符,若緊接getline
,會讀入一個空行。
解決:每次切換前調用fin.ignore()
跳過殘留的'\n'
。
四、二進制文件 I/O
當讀寫原始字節或 POD 結構時,選用二進制模式并使用 read
/write
:
struct Record { int id; double value; };void writeRecords(const std::vector<Record>& recs) {std::ofstream fout("rec.bin", std::ios::out|std::ios::binary);fout.write(reinterpret_cast<const char*>(recs.data()),recs.size()*sizeof(Record));
}std::vector<Record> readRecords() {std::ifstream fin("rec.bin", std::ios::in|std::ios::binary);fin.seekg(0, std::ios::end);std::size_t size = fin.tellg() / sizeof(Record);fin.seekg(0, std::ios::beg);std::vector<Record> recs(size);fin.read(reinterpret_cast<char*>(recs.data()),size*sizeof(Record));return recs;
}
- 注意:直接寫整體
vector
只對標準布局(POD)類型安全。若含指針或非平凡類型,需循環寫每個字段或序列化。
五、隨機訪問(定位)
-
seekg
/seekp
:設置讀/寫位置fin.seekg(offset, std::ios::beg|std::ios::cur|std::ios::end); fout.seekp(...);
-
tellg
/tellp
:返回當前位置auto pos = fin.tellg();
-
注意:文本模式下各種平臺會對換行做轉換,
seek
/tell
不保證以字節為單位精確跳轉;二進制模式下則如你所見。
六、錯誤處理與異常
1. 狀態位檢查
if (!fin) { /* 打開失敗或已處于錯誤狀態 */ }
if (fin.eof()) { /* 到達末尾 */ }
if (fin.fail()) { /* 格式錯誤或開關失敗 */ }
2. 異常模式
fin.exceptions(std::ios::failbit | std::ios::badbit);
try {int x;fin >> x; // 失敗時拋 std::ios_base::failure
} catch (const std::ios_base::failure& e) {std::cerr << "I/O 異常: " << e.what() << "\n";
}
3. 資源管理
-
文件流析構時會自動
close()
,但若需提前關閉或檢測錯誤,可顯式:fin.close(); if (fin.fail()) std::cerr<<"關閉失敗\n";
七、性能優化
-
同步關閉
std::ios::sync_with_stdio(false); std::cin.tie(nullptr);
可加速與 C 風格 I/O 的混用場景。
-
緩沖區大小
- 默認緩沖區通常足夠;若需高性能可自定義:重載流的
rdbuf()->pubsetbuf()
。
- 默認緩沖區通常足夠;若需高性能可自定義:重載流的
-
減少臨時與拷貝
- 盡量一次性
read
/write
大塊數據; - 對文本分詞解析可用
std::string_view
和std::getline
配合std::stringstream
。
- 盡量一次性
-
避免頻繁開關文件
- 對同一文件的多次寫入,宜在同一流對象上完成;若交替讀寫,用
fstream
且調用seek
/flush
而非反復構造流。
- 對同一文件的多次寫入,宜在同一流對象上完成;若交替讀寫,用
八、綜合示例
#include <iostream>
#include <fstream>
#include <vector>
#include <string>struct Record { int id; double value; };int main() {// 1. 文本寫入{std::ofstream log("app.log", std::ios::out|std::ios::app);if (!log) throw std::runtime_error("無法打開日志");log << "程序啟動\n";}// 2. 文本讀取與解析{std::ifstream fin("config.txt");if (!fin) {std::cerr<<"配置文件打開失敗\n";} else {std::string line;while (std::getline(fin, line)) {// 跳過空行和注釋if (line.empty() || line[0]=='#') continue;std::cout<<"配置: "<<line<<"\n";}}}// 3. 二進制讀寫std::vector<Record> recs = {{1,1.23},{2,4.56},{3,7.89}};{std::ofstream fout("data.bin", std::ios::out|std::ios::binary);fout.write(reinterpret_cast<const char*>(recs.data()),recs.size()*sizeof(Record));}{std::ifstream fin("data.bin", std::ios::in|std::ios::binary);fin.seekg(0, std::ios::end);size_t n = fin.tellg()/sizeof(Record);fin.seekg(0);std::vector<Record> buf(n);fin.read(reinterpret_cast<char*>(buf.data()), n*sizeof(Record));std::cout<<"讀取 "<<buf.size()<<" 條記錄\n";}return 0;
}
九、注意事項匯總
- 選擇合適模式:文本 vs 二進制;是否截斷或追加。
- 檢查流狀態:打開后、讀寫后、關閉后都應檢查
fail()
/bad()
/eof()
。 - 混合讀寫:使用
std::fstream
并在切換前調用flush()
/seekg()
/seekp()
。 - 異常安全:開啟異常模式或自行檢測,避免未捕獲錯誤導致數據不一致。
- 平臺差異:文本模式下換行轉換、字符編碼(如 Windows 的 CRLF)可能影響跨平臺行為。