C++文件流操作方式
引言
在 C 語言中,文件操作依賴于 FILE* 和一系列函數(shù)(fopen、fread、fwrite、fprintf 等)。這種方式雖然功能齊全,但存在類型不安全、容易忘記關(guān)閉文件、錯誤處理繁瑣等問題。
C++ 引入了流(Stream)的概念,將輸入輸出抽象為"數(shù)據(jù)流",通過統(tǒng)一的接口操作不同的設(shè)備(鍵盤、屏幕、文件、字符串等)。流體系基于面向?qū)ο笤O(shè)計,提供了類型安全、可擴展的 IO 操作方式。

第一部分:IO 流類層次體系
一、完整繼承關(guān)系

二、常用頭文件與類
| 頭文件 | 包含的類 | 用途 |
|---|---|---|
<iostream> | cin, cout, cerr, clog | 標準控制臺 IO |
<fstream> | ifstream, ofstream, fstream | 文件 IO |
<sstream> | istringstream, ostringstream, stringstream | 字符串 IO |
<iomanip> | 操縱符函數(shù)(setw, setprecision 等) | 格式控制 |
三、四個標準流對象
#include <iostream> using namespace std; // cin — istream 對象,關(guān)聯(lián)標準輸入(鍵盤) // cout — ostream 對象,關(guān)聯(lián)標準輸出(屏幕) // cerr — ostream 對象,關(guān)聯(lián)標準錯誤(無緩沖) // clog — ostream 對象,關(guān)聯(lián)標準日志(有緩沖)
cerr 和 clog 的區(qū)別:
cerr << "錯誤信息" << endl; // 立即輸出(無緩沖) clog << "日志信息" << endl; // 緩沖后輸出
第二部分:標準輸入流詳解
一、逐字符讀取
#include <iostream>
using namespace std;
int main() {
char ch;
// 方式1:get() 獲取一個字符(返回 istream 引用)
cin.get(ch);
// 方式2:get() 返回字符的 ASCII 值(int 類型)
int ch2 = cin.get();
cout << ch << ", " << ch2 << endl;
return 0;
}get() 的兩種重載對比:
| 重載形式 | 返回值 | 用途 |
|---|---|---|
cin.get(ch) | istream& | 讀取字符存入 ch |
cin.get() | int | 返回讀取字符的 ASCII 值(-1 表示 EOF) |
二、逐行讀取
char buf[128] = {0};
// 方式1:getline(),遇到換行符結(jié)束
cin.getline(buf, 128);
// 方式2:getline() 指定分隔符
cin.getline(buf, 128, '\n'); // 第三個參數(shù)是分隔符
// 方式3:read() 讀取指定字節(jié)數(shù)
cin.read(buf, 10);getline() 與 >> 的區(qū)別:
| 方法 | 遇到空格 | 遇到換行 | 讀取換行符 | 安全性 |
|---|---|---|---|---|
cin >> buf | 停止 | 停止 | 不讀?。粼诰彌_區(qū)) | ? 無長度限制 |
cin.getline(buf, n) | 繼續(xù) | 停止 | 讀取并丟棄 | ? 有長度限制 |
三、自定義讀行函數(shù)
#include <string>
// C 風格字符串讀取
long readline(char* buf, int maxSize) {
char ch;
int len = 0;
while (true) {
cin.get(ch);
if (ch == '\n' || len >= maxSize) break;
buf[len++] = ch;
}
return len;
}
// C++ string 讀取
long readString(string& s) {
char ch;
while (true) {
cin.get(ch);
if (ch == '\n') break;
s += ch;
}
return s.size();
}四、緩沖區(qū)管理
cin.ignore(); // 忽略一個字符(清空緩沖區(qū)) cin.ignore(100); // 忽略最多 100 個字符 cin.ignore(100, '\n'); // 忽略直到換行符(最多 100 個) cin.clear(); // 清除錯誤狀態(tài)標志
常見場景:cin >> n 后殘留的換行符需要清理
int n; cin >> n; cin.ignore(); // 清除殘留的 '\n' // 然后安全讀取下一行 string line; getline(cin, line);
第三部分:標準輸出流詳解
一、基本輸出方法
// put() — 輸出單個字符
cout.put('A');
cout.put('\n');
// write() — 輸出指定長度的字符串
cout.write("hello", 5); // 輸出5個字符
// flush() — 強制刷新緩沖區(qū)
cout.flush();
// endl — 輸出換行并刷新
cout << endl; // 等價于 cout << '\n' << flush;輸出緩沖區(qū)的四種刷新時機:
| 刷新模式 | 觸發(fā)條件 |
|---|---|
| 滿刷新 | 緩沖區(qū)滿時自動刷新 |
| 行刷新 | 遇到換行符 \n 時刷新 |
| 程序退出 | exit() 或 return 時刷新 |
| 強制刷新 | 調(diào)用 flush() 或 endl |
二、格式控制 — 進制輸出
#include <iomanip> int n = 100; // 使用操縱符 cout << showbase << hex << n << endl; // 0x64 (十六進制,帶前綴) cout << oct << n << endl; // 0144 (八進制) cout << dec << n << endl; // 100 (十進制) // showbase — 顯示進制前綴(0x 或 0) // noshowbase — 取消進制前綴
| 操縱符 | 效果 |
|---|---|
hex | 十六進制輸出 |
oct | 八進制輸出 |
dec | 十進制輸出(默認) |
showbase | 顯示進制前綴 |
noshowbase | 取消進制前綴 |
uppercase | 十六進制字母大寫 |
nouppercase | 十六進制字母小寫(默認) |
三、格式控制 — 使用 flags
// 獲取當前標志 ios::fmtflags old_flags = cout.flags(); // 設(shè)置新標志(會覆蓋舊標志) cout.flags(ios::showbase | ios::hex); cout << 90 << endl; // 0x5a // 恢復(fù)舊標志 cout.flags(old_flags);
常用格式標志:
| 標志 | 含義 |
|---|---|
ios::showbase | 顯示進制前綴 |
ios::hex | 十六進制 |
ios::oct | 八進制 |
ios::dec | 十進制 |
ios::left | 左對齊 |
ios::right | 右對齊 |
ios::fixed | 固定小數(shù)位 |
ios::scientific | 科學計數(shù)法 |
四、格式控制 — 寬度和填充
cout.width(20); // 設(shè)置輸出寬度為 20(只影響下一個輸出)
cout.fill('*'); // 設(shè)置填充字符為 '*'
cout << left; // 左對齊
cout << "hi,disen!" << endl;
// 輸出:hi,disen!***********
// 使用操縱符
cout << setw(20) << setfill('*') << left << "hi,disen!" << endl;| 方法/操縱符 | 作用 | 生效范圍 |
|---|---|---|
width(n) / setw(n) | 設(shè)置輸出寬度 | 只影響下一個輸出 |
fill(c) / setfill(c) | 設(shè)置填充字符 | 持久生效 |
left | 左對齊 | 持久生效 |
right | 右對齊 | 持久生效 |
五、格式控制 — 浮點數(shù)精度
#include <iomanip> double d = 1.2345678; // 設(shè)置精度 cout << setprecision(3) << d << endl; // 1.23 cout << setprecision(1) << 2.459 << endl; // 2 // 固定小數(shù)位模式 cout << fixed << setprecision(2) << 1.345678 << endl; // 1.35 // 科學計數(shù)法模式 cout << scientific << 123.456 << endl; // 1.234560e+02 // 默認模式 cout << defaultfloat << 99.2389 << endl;
| 操縱符 | 精度含義 |
|---|---|
defaultfloat | 有效數(shù)字位數(shù)(默認) |
fixed | 小數(shù)點后位數(shù) |
scientific | 小數(shù)點后位數(shù)(科學計數(shù)法) |
第四部分:文件流詳解
一、文件打開模式
#include <fstream> // 定義在 ios 中 ios::in // 讀模式(ifstream 默認) ios::out // 寫模式(ofstream 默認) ios::app // 追加模式(寫入到文件末尾) ios::ate // 打開時定位到文件末尾 ios::trunc // 打開時清空文件內(nèi)容 ios::binary // 二進制模式
組合使用:
// 讀寫模式,二進制
fstream fs("data.dat", ios::in | ios::out | ios::binary);
// 寫模式,追加
ofstream fs("log.txt", ios::out | ios::app);二、文件寫入
#include <fstream>
#include <cstring>
int main() {
ofstream fs("a.txt", ios::out);
if (!fs.good()) {
cout << "打開文件失敗" << endl;
return -1;
}
char line[128] = {0};
while (true) {
cin.getline(line, 128);
if (strlen(line) == 0) break;
fs.write(line, strlen(line));
fs.write("\n", 1);
}
fs.close();
return 0;
}三、文件讀取
#include <fstream>
#include <string>
int main() {
ifstream fs("a.txt"); // 默認 ios::in
if (!fs.good()) return -1;
// 方式1:逐行讀取
string line;
while (getline(fs, line)) {
cout << line << endl;
}
// 方式2:逐詞讀取
string word;
while (fs >> word) {
cout << word << endl;
}
fs.close();
return 0;
}注意:代碼中 while (!fs.eof()) 存在一個常見陷阱:
// ? 有問題的寫法
while (!fs.eof()) {
fs.getline(buf, 128);
cout << buf << endl; // 可能輸出兩次最后一行
}
// ? 正確的寫法(將讀取操作放在循環(huán)條件中)
while (fs.getline(buf, 128)) {
cout << buf << endl;
}原理:eof() 只在嘗試讀取失敗后才變?yōu)?true,不是"預(yù)知"文件結(jié)束。
四、二進制文件讀寫
struct Person {
int pid;
char name[32];
void hi() {
cout << "pid: " << pid << ", name: " << name << endl;
}
};
int main() {
Person p1{1001, "Lucy"}, p2{1002, "Disen"};
// 二進制寫入
fstream fs("b.dat", ios::out | ios::binary);
if (!fs.good()) return -1;
fs.write(reinterpret_cast<char*>(&p1), sizeof(Person));
fs.write(reinterpret_cast<char*>(&p2), sizeof(Person));
fs.close();
// 二進制讀取 — 先獲取文件大小
fstream ifs("b.dat", ios::in | ios::binary);
if (!ifs.good()) return -1;
ifs.seekg(0, ios::end); // 移到文件末尾
auto len = ifs.tellg(); // 獲取當前位置(即文件大?。?
int n = len / sizeof(Person);
cout << "Person 個數(shù): " << n << endl;
ifs.seekg(0, ios::beg); // 移回文件開始
for (int i = 0; i < n; i++) {
Person p;
ifs.read(reinterpret_cast<char*>(&p), sizeof(Person));
p.hi();
}
ifs.close();
return 0;
}五、文件流狀態(tài)檢查
ifstream fs("test.txt");
// 方法1:good()
if (fs.good()) {
// 文件打開成功,且沒有錯誤
}
// 方法2:is_open()
if (fs.is_open()) {
// 文件已成功打開
}
// 方法3:直接用作布爾值
if (!fs) {
// 文件打開失敗
}| 狀態(tài)函數(shù) | 含義 |
|---|---|
good() | 流狀態(tài)正常,無任何錯誤 |
eof() | 到達文件末尾 |
fail() | 操作失敗(可恢復(fù)) |
bad() | 嚴重錯誤(不可恢復(fù)) |
is_open() | 文件是否打開 |
六、文件指針定位
ifstream fs("data.txt");
// seekg() — 移動讀指針
fs.seekg(0, ios::beg); // 移到開頭
fs.seekg(0, ios::end); // 移到末尾
fs.seekg(10, ios::cur); // 從當前位置后移10字節(jié)
// tellg() — 獲取讀指針位置
auto pos = fs.tellg();
// seekp() — 移動寫指針(ofstream)
// tellp() — 獲取寫指針位置| 定位標志 | 含義 |
|---|---|
ios::beg | 相對于文件開頭 |
ios::cur | 相對于當前位置 |
ios::end | 相對于文件末尾 |
第五部分:C 與 C++ IO 對照表
| 操作 | C 語言 | C++ 流 |
|---|---|---|
| 打開文件 | fopen("a.txt", "r") | ifstream fs("a.txt") |
| 關(guān)閉文件 | fclose(fp) | fs.close() 或析構(gòu)自動 |
| 讀字符 | fgetc(fp) | fs.get(ch) |
| 讀一行 | fgets(buf, n, fp) | fs.getline(buf, n) |
| 寫字符 | fputc(ch, fp) | fs.put(ch) |
| 寫一行 | fputs(str, fp) | fs << str |
| 二進制讀 | fread(buf, sz, n, fp) | fs.read(buf, n) |
| 二進制寫 | fwrite(buf, sz, n, fp) | fs.write(buf, n) |
| 文件指針 | fseek(fp, 0, SEEK_END) | fs.seekg(0, ios::end) |
| 獲取位置 | ftell(fp) | fs.tellg() |
| 錯誤檢查 | 檢查返回值 | fs.good() / !fs |
| 格式化輸出 | fprintf(fp, "%x", n) | fs << hex << n |
總結(jié)
一、IO 流體系核心類

二、文件操作通用流程
1. 創(chuàng)建流對象
ifstream fs("filename", ios::in);2. 檢查是否打開成功
if (!fs.good()) { /* 錯誤處理 */ }3. 讀取/寫入操作
fs >> data; 或 fs.read(buf, size);4. 關(guān)閉文件
fs.close();
// 或者依賴析構(gòu)函數(shù)自動關(guān)閉
三、關(guān)鍵記憶點
| 要點 | 說明 |
|---|---|
cin.ignore() | 清除緩沖區(qū)殘留 |
cout.width(n) | 只影響下一個輸出 |
cout.fill(c) | 持久生效 |
while (getline(fs, line)) | 正確的逐行讀取方式 |
reinterpret_cast<char*>(&obj) | 二進制讀寫結(jié)構(gòu)體時的類型轉(zhuǎn)換 |
fs.seekg(0, ios::end) | 計算文件大小 |
| RAII 特性 | 流對象析構(gòu)時自動關(guān)閉文件 |
C++ IO 流體系是一個設(shè)計精巧的面向?qū)ο罂蚣埽鼘⒖刂婆_、文件、字符串等不同設(shè)備的輸入輸出統(tǒng)一到一個繼承層次中。理解流體系的關(guān)鍵在于:
- 繼承層次:
istream/ostream是所有輸入輸出流的基類 - 格式控制:通過操縱符(manipulator)控制輸出格式
- RAII 機制:流對象析構(gòu)時自動關(guān)閉文件,避免資源泄漏
- 狀態(tài)檢查:始終檢查
good()或直接使用流對象的布爾值判斷
到此這篇關(guān)于C++文件流操作方式的文章就介紹到這了,更多相關(guān)C++文件流操作內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用WindowsAPI實現(xiàn)播放PCM音頻的方法
這篇文章主要介紹了使用WindowsAPI實現(xiàn)播放PCM音頻的方法,很實用的一個功能,需要的朋友可以參考下2014-08-08

