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

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

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

二、文件操作通用流程
1. 創(chuàng)建流對(duì)象
ifstream fs("filename", ios::in);2. 檢查是否打開成功
if (!fs.good()) { /* 錯(cuò)誤處理 */ }3. 讀取/寫入操作
fs >> data; 或 fs.read(buf, size);4. 關(guān)閉文件
fs.close();
// 或者依賴析構(gòu)函數(shù)自動(dòng)關(guān)閉
三、關(guān)鍵記憶點(diǎn)
| 要點(diǎn) | 說明 |
|---|---|
cin.ignore() | 清除緩沖區(qū)殘留 |
cout.width(n) | 只影響下一個(gè)輸出 |
cout.fill(c) | 持久生效 |
while (getline(fs, line)) | 正確的逐行讀取方式 |
reinterpret_cast<char*>(&obj) | 二進(jìn)制讀寫結(jié)構(gòu)體時(shí)的類型轉(zhuǎn)換 |
fs.seekg(0, ios::end) | 計(jì)算文件大小 |
| RAII 特性 | 流對(duì)象析構(gòu)時(shí)自動(dòng)關(guān)閉文件 |
C++ IO 流體系是一個(gè)設(shè)計(jì)精巧的面向?qū)ο罂蚣?,它將控制臺(tái)、文件、字符串等不同設(shè)備的輸入輸出統(tǒng)一到一個(gè)繼承層次中。理解流體系的關(guān)鍵在于:
- 繼承層次:
istream/ostream是所有輸入輸出流的基類 - 格式控制:通過操縱符(manipulator)控制輸出格式
- RAII 機(jī)制:流對(duì)象析構(gòu)時(shí)自動(dòng)關(guān)閉文件,避免資源泄漏
- 狀態(tài)檢查:始終檢查
good()或直接使用流對(duì)象的布爾值判斷
到此這篇關(guān)于C++文件流操作方式的文章就介紹到這了,更多相關(guān)C++文件流操作內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++超詳細(xì)梳理lambda和function的使用方法
C++在C11標(biāo)準(zhǔn)中引入了匿名函數(shù),即沒有名字的臨時(shí)函數(shù),又稱之為lambda表達(dá)式.lambda表達(dá)式 實(shí)質(zhì)上是創(chuàng)建一個(gè)匿名函數(shù)/對(duì)象,這篇文章主要介紹了lambda和function的使用方法2022-08-08
C\C++實(shí)現(xiàn)讀寫二進(jìn)制文件的方法詳解
這篇文章主要為大家詳細(xì)介紹了C\C++實(shí)現(xiàn)讀寫二進(jìn)制文件的方法,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以了解一下2023-03-03
C++中map_set的封裝實(shí)現(xiàn)整體代碼
這篇文章主要介紹了C++中map_set封裝實(shí)現(xiàn)的相關(guān)資料,包括源碼解析、仿函數(shù)的使用、迭代器的設(shè)計(jì)以及const迭代器的實(shí)現(xiàn),文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-12-12
使用WindowsAPI實(shí)現(xiàn)播放PCM音頻的方法
這篇文章主要介紹了使用WindowsAPI實(shí)現(xiàn)播放PCM音頻的方法,很實(shí)用的一個(gè)功能,需要的朋友可以參考下2014-08-08

