C++进阶教程:文件流与字符串流
本文摘要
1.3 读取文件1.4 打开模式| 模式 | 含义 || — | — | — || | 读模式(ifstream 默认) || | 写模式(ofstream 默认,截断) || | 追加(不截断)…
本文是 C++ 系列教程的第 10 篇。上一篇讲解了智能指针,本篇讲解文件与流操作:fstream 文本与二进制读写、流状态检查、getline、sstream 字符串流、C++17 filesystem 入门。
一、文件流基础
1.1 三种文件流
| 流类型 | 头文件 | 用途 |
|---|---|---|
ifstream |
<fstream> |
读文件(输入流) |
ofstream |
<fstream> |
写文件(输出流) |
fstream |
<fstream> |
读写文件 |
1.2 写入文件
#include <iostream>#include <fstream>using namespace std;
int main() { // 方式一:先定义再打开 ofstream fout; fout.open("test.txt"); // 默认覆盖写 fout << "Hello, File!" << endl; fout << 42 << " " << 3.14 << endl; fout.close(); // 记得关闭
// 方式二:构造时打开(推荐) ofstream fout2("data.txt", ios::app); // 追加模式 fout2 << "追加一行" << endl; fout2.close();
cout << "文件写入完成" << endl; return 0;}1.3 读取文件
#include <iostream>#include <fstream>#include <string>using namespace std;
int main() { ifstream fin("test.txt"); if (!fin) { // 检查是否成功打开 cout << "文件打开失败!" << endl; return 1; }
string line; // 逐行读取 while (getline(fin, line)) { cout << line << endl; } fin.close(); return 0;}1.4 打开模式
| 模式 | 含义 |
| — | — | — |
| ios::in | 读模式(ifstream 默认) |
| ios::out | 写模式(ofstream 默认,截断) |
| ios::app | 追加(不截断) |
| ios::ate | 打开后定位到文件尾 |
| ios::trunc | 截断文件 |
| ios::binary | 二进制模式 |
二、流状态检查
2.1 流状态标志
#include <iostream>#include <fstream>using namespace std;
int main() { ifstream fin("不存在的文件.txt"); if (fin.fail()) { // 打开失败 cout << "打开失败 (failbit)" << endl; }
fin.open("test.txt"); if (fin.good()) { // 状态正常 cout << "文件状态良好" << endl; }
// 读取到末尾 string s; while (fin >> s) { } // 读取直到失败 if (fin.eof()) { // 到达文件末尾 cout << "已读到文件���尾 (eofbit)" << endl; } fin.close(); return 0;}2.2 打开文件判断的两种写法
#include <iostream>#include <fstream>using namespace std;
int main() { // 写法一:!fin ifstream fin1("a.txt"); if (!fin1) { cout << "方式一检测到打开失败" << endl; }
// 写法二:!fin.is_open() ifstream fin2("b.txt"); if (!fin2.is_open()) { cout << "方式二检测到打开失败" << endl; } return 0;}三、文本读写实战
3.1 学生成绩文件读写
#include <iostream>#include <fstream>#include <string>#include <vector>using namespace std;
struct Student { string name; int score;};
// 写入学生数据void saveStudents(const vector<Student> &students, const string &filename) { ofstream fout(filename); for (const auto &s : students) { fout << s.name << " " << s.score << endl; } fout.close(); cout << "已保存 " << students.size() << " 条记录" << endl;}
// 读取学生数据vector<Student> loadStudents(const string &filename) { vector<Student> result; ifstream fin(filename); if (!fin) { cout << "读取失败" << endl; return result; } Student s; while (fin >> s.name >> s.score) { // 格式化读取 result.push_back(s); } fin.close(); return result;}
int main() { vector<Student> students = { {"张三", 88}, {"李四", 92}, {"王五", 76} }; saveStudents(students, "students.txt");
auto loaded = loadStudents("students.txt"); for (const auto &s : loaded) { cout << s.name << " " << s.score << "分" << endl; } return 0;}3.2 读取整行与单词
#include <iostream>#include nclude <sstream>#include <string>using namespace std;
int main() { ofstream fout("mixed.txt"); fout << "Alice 25 88.5" << endl; fout << "Bob 30 95.0" << endl; fout.close();
ifstream fin("mixed.txt"); string line; while (getline(fin, line)) { // 读整行 stringstream ss(line); // 字符串流解析 string name; int age; double score; ss >> name >> age >> score; // 从行中提取字段 cout << name << " " << age << "岁 " << score << "分" << endl; } return 0;}四、二�
�制文件读写
4.1 二进制写入
#include <iostream>#include <fstream>using namespace std;
struct Record { int id; char name[20]; double salary;};
int main() { Record records[] = { {1, "张三", 8000.5}, {2, "李四", 9500.0}, {3, "王五", 7500.75} };
// 二进制写入 ofstream fout("records.dat", ios::binary); fout.write(reinterpret_cast<char *>(records), sizeof(records)); fout.close(); cout << "二进制写入完成" << endl; return 0;}4.2 二进制读取
#include <iostream>#include <fstream>using namespace std;
struct Record { int id; char name[20]; double salary;};
int main() { ifstream fin("records.dat", ios::binary); if (!fin) { cout << "打开失败" << endl; return 1; }
Record r; while (fin.read(reinterpret_cast<char *>(&r), sizeof(Record))) { cout << r.id << " " << r.name << " " << r.salary << endl; } fin.close(); return 0;}4.3 文本 vs 二进制对比
| 维度 | 文本模式 | 二进制模式 |
|---|---|---|
| 可读性 | 人类可读 | 不可读 |
| 大小 | 较大 | 紧凑 |
| 精度 | 可能损失(浮点) | 精确保留 |
| 跨平台 | 换行符转换 | 按原样 |
| 适用 | 配置、日志 | 程序数据、图像 |
五、字符串流 sstream
5.1 字符串拼接与格式化
#include <iostream>#include <sstream>#include <string>using namespace std;
int main() { // 字符串拼接 stringstream ss; ss << "姓名: " << "张三" << ", 年龄: " << 25 << ", 分数: " << 88.5; string result = ss.str(); // 转为 string cout << result << endl;
// 清理复用 ss.str(""); // 清空内容 ss.clear(); // 重置状态 ss << "重新写入"; cout << ss.str() << endl; return 0;}5.2 字符串解析
#include <iostream>#include <sstream>#include <string>using namespace std;
int main() { // 解析 CSV 格式 string csv = "Alice,25,88.5"; stringstream ss(csv); string name; int age; double score; char comma1, comma2;
ss >> name >> comma1 >> age >> comma2 >> score; cout << "解析结果: " << name << " " << age << " " << score << endl;
// 字符串转数字 string numStr = "3.14159";
double pi; stringstream(numStr) >> pi; cout << "π = " << pi << endl;
// 数字转字符串 int value = 42; string valueStr; stringstream().swap(ss); ss << value; valueStr = ss.str(); cout << "字符串: " << valueStr << endl; return 0;}5.3 实用的格式化工具函数
#include <iostream>#include <sstream>#include <iomanip>using namespace std;
// 通用转换函数template <typename T>string toString(const T &value) { stringstream ss; ss << value; return ss.str();}
template <typename T>bool fromString(const string &str, T &value) { stringstream ss(str); return !!(ss >> value); // 转换成功返回 true}
int main() { // 格式化数字 stringstream ss; ss << fixed << setprecision(2) << 3.14159; cout << "保留两位: " << ss.str() << endl; // 3.14
// 通用转换 cout << "toString: " << toString(123) << endl; cout << "toString: " << toString(3.14) << endl;
double d; if (fromString("99.5", d)) { cout << "fromString: " << d << endl; // 99.5 } return 0;}六、C++17 filesystem 入门
6.1 路径与文件信息
#include <iostream>#include <filesystem>using namespace std;namespace fs = filesystem; // 命名空间别名
int main() { // 当前路径 cout << "当前目录: " << fs::current_path() << endl;
// 路径拼接 fs::path p = fs::current_path() / "test.txt"; cout << "完整路径: " << p << endl; cout << "文件名: " << p.filename() << endl; cout << "扩展名: " << p.extension() << endl; cout << "父目录: " << p.parent_path() << endl; return 0;}6.2 目录操作
#include <iostream>#include <filesystem>using namespace std;namespace fs = filesystem;
int main() { // 创建目录 fs::create_directory("mydir"); cout << "目录存在: " << fs::exists("mydir") << endl;
// 递归创建 fs::create_directories("a/b/c"); cout << "递归目录存在: " << fs::exists("a/b/c") << endl;
// 遍历目录 cout << "mydir 内容:" << endl; for (const auto &entry : fs::directory_iterator("mydir")) { cout << " " << entry.path().filename() << endl; }
// 删除 fs::remove("mydir"); fs::remove_all("a"); // 递归删除 cout << "清理完成" << endl; return 0;}```
### 6.3 文件大小与状态
```cpp#include <iostream>#include <fstream>#include <filesystem>using namespace std;namespace fs = filesystem;
int main() { // 创建一个文件 ofstream fout("temp.bin", ios::binary); fout.write("Hello Filesystem", 16); fout.close();
// 查询文件状态 fs::path p("temp.bin"); if (fs::exists(p)) { cout << "文件存在" << endl; cout << "大小: " << fs::file_size(p) << " 字节" << endl; cout << "是否为文件: " << fs::is_regular_file(p) << endl; cout << "最后修改时间: " << fs::last_write_time(p).time_since_epoch().count() << endl; } fs::remove(p); return 0;}七、实战:配置读写器
综合本篇知识,实现配置文件读写器:
#include <iostream>#include <fstream>#include <sstream>#include <map>#include <string>using namespace std;
class Config {private: map<string, string> data;
public: // 从配置文件加载 bool load(const string &filename) { ifstream fin(filename); if (!fin) return false; string line; while (getline(fin, line)) { if (line.empty() || line[0] == '#') continue; // 跳过空行和注释 size_t pos = line.find('='); if (pos != string::npos) { string key = line.substr(0, pos); string value = line.substr(pos + 1); data[key] = value; } } return true; }
// 保存到配置文件 void save(const string &filename) { ofstream fout(filename); for (const auto &kv : data) { fout << kv.first << "=" << kv.second << endl; } cout << "配置已保存" << endl; }
string get(const string &key, const string &def = "") const { auto it = data.find(key); return it != data.end() ? it->second : def; }
void set(const string &key, const string &value) { data[key] = value; }};
int main() { Config config;
// 写入配置 config.set("host", "localhost"); config.set("port", "8080"); config.set("timeout", "30"); config.save("app.conf");
// 重新读取 Config loaded; if (loaded.load("app.conf")) { cout << "host = " << loaded.get("host") << endl; cout << "port = " << loaded.get("port") << endl; cout << "timeout = " << loaded.get("timeout") << endl; cout << "不存在项 = " << loaded.get("nokey", "默认值") << endl; } return 0;}总结
本篇讲解了文件流的三种类型与打开模式、流状态检查、文本与二进制读写、字符串流 sstream 的格式化与解析、C++17 filesystem 的路径目录操作,并用配置读写器串联实战。重点掌握:打开文件的失败检查、getline 与 >> 的区别、文本与二进制的选择、stringstream 的类型转换技巧。
至此 C++ 进阶阶段(6-10 篇)完成!下一篇将进入 STL 阶段:容器总览与序列容器,敬请期待!
C++进阶教程:文件流与字符串流
作者:Wang Shijie
本文链接:https://wsj-blog.pages.dev/posts/c-jin-jie-jiao-cheng-wen-jian-liu-yu-zi-fu-chuan-liu/
本文采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
留言评论
评论资源加载失败,请重试。