一、文件流类概述
C++ 标准库提供了三个主要的文件流类:
- ifstream (输入文件流):用于从文件读取数据
- ofstream (输出文件流):用于向文件写入数据
- fstream (文件流):既可读又可写
这些类都继承自 iostream
类,因此可以使用 <<
和 >>
操作符进行格式化I/O操作。
二、文件打开与关闭
1. 打开文件
cpp
#include <fstream>
using namespace std;
// 方法1:先声明再打开
ofstream outFile;
outFile.open("output.txt");
// 方法2:声明时直接打开
ifstream inFile("input.txt");
// 方法3:使用fstream
fstream ioFile;
ioFile.open("data.txt", ios::in | ios::out);
2. 文件打开模式
模式标志 | 描述 |
---|---|
ios::in | 以读取方式打开 |
ios::out | 以写入方式打开 |
ios::app | 追加模式,所有写入都追加到文件末尾 |
ios::ate | 打开文件后定位到文件末尾 |
ios::trunc | 如果文件存在则清空内容 |
ios::binary | 二进制模式 |
组合示例:
cpp
// 以写入和追加模式打开
ofstream logFile("log.txt", ios::out | ios::app);
// 以读写和二进制模式打开
fstream dataFile("data.dat", ios::in | ios::out | ios::binary);
3. 关闭文件
cpp
outFile.close();
inFile.close();
ioFile.close();
注意:虽然流对象析构时会自动关闭文件,但显式关闭是个好习惯。
三、文件状态检查
cpp
ifstream file("data.txt");
if (file.is_open()) {
// 文件成功打开
} else {
// 文件打开失败
}
// 检查流状态
if (file.good()) {
// 流状态良好
}
if (file.fail()) {
// 操作失败(非致命错误)
}
if (file.bad()) {
// 严重错误
}
if (file.eof()) {
// 到达文件末尾
}
四、文本文件操作
1. 写入文本文件
cpp
ofstream out("output.txt");
if (out.is_open()) {
out << "第一行文本" << endl;
out << 123 << " " << 3.14 << endl;
out << "这是另一行文本" << endl;
out.close();
} else {
cerr << "无法打开文件!" << endl;
}
2. 读取文本文件
逐行读取:
cpp
ifstream in("input.txt");
string line;
if (in.is_open()) {
while (getline(in, line)) {
cout << line << endl;
}
in.close();
}
逐词读取:
cpp
ifstream in("input.txt");
string word;
if (in.is_open()) {
while (in >> word) {
cout << word << endl;
}
in.close();
}
格式化读取:
cpp
ifstream in("data.txt");
int id;
double value;
string name;
if (in.is_open()) {
while (in >> id >> value >> name) {
cout << "ID: " << id << ", Value: " << value
<< ", Name: " << name << endl;
}
in.close();
}
五、二进制文件操作
1. 写入二进制数据
cpp
struct Record {
int id;
char name[50];
double salary;
};
Record emp = {101, "John Doe", 4500.50};
ofstream out("employees.dat", ios::binary);
if (out.is_open()) {
out.write(reinterpret_cast<char*>(&emp), sizeof(Record));
out.close();
}
2. 读取二进制数据
cpp
Record emp;
ifstream in("employees.dat", ios::binary);
if (in.is_open()) {
in.read(reinterpret_cast<char*>(&emp), sizeof(Record));
cout << "ID: " << emp.id << endl;
cout << "Name: " << emp.name << endl;
cout << "Salary: " << emp.salary << endl;
in.close();
}
3. 二进制文件随机访问
cpp
fstream file("data.dat", ios::in | ios::out | ios::binary);
// 写入多个记录
Record records[3] = {
{101, "Alice", 5000},
{102, "Bob", 6000},
{103, "Charlie", 7000}
};
file.write(reinterpret_cast<char*>(records), 3 * sizeof(Record));
// 直接读取第二个记录
Record emp;
file.seekg(1 * sizeof(Record), ios::beg);
file.read(reinterpret_cast<char*>(&emp), sizeof(Record));
// 修改第三个记录
emp = {103, "David", 7500};
file.seekp(2 * sizeof(Record), ios::beg);
file.write(reinterpret_cast<char*>(&emp), sizeof(Record));
file.close();
六、文件定位
1. 获取当前位置
cpp
streampos pos = file.tellg(); // 获取读取位置
streampos pos = file.tellp(); // 获取写入位置
2. 设置位置
cpp
// 绝对定位
file.seekg(0, ios::beg); // 移动到文件开头
file.seekg(0, ios::end); // 移动到文件末尾
// 相对定位
file.seekg(10, ios::cur); // 从当前位置向前移动10字节
file.seekg(-5, ios::cur); // 从当前位置向后移动5字节
七、实用文件操作函数
1. 检查文件是否存在
cpp
#include <sys/stat.h>
bool fileExists(const string& filename) {
struct stat buffer;
return (stat(filename.c_str(), &buffer) == 0);
}
2. 获取文件大小
cpp
streampos getFileSize(const string& filename) {
ifstream file(filename, ios::ate | ios::binary);
return file.tellg();
}
3. 复制文件
cpp
bool copyFile(const string& source, const string& dest) {
ifstream src(source, ios::binary);
ofstream dst(dest, ios::binary);
if (!src || !dst) {
return false;
}
dst << src.rdbuf();
return true;
}
4. 读取CSV文件
cpp
void readCSV(const string& filename) {
ifstream file(filename);
string line;
while (getline(file, line)) {
stringstream ss(line);
string cell;
vector<string> row;
while (getline(ss, cell, ',')) {
row.push_back(cell);
}
// 处理行数据
for (const auto& val : row) {
cout << val << "\t";
}
cout << endl;
}
}
八、错误处理最佳实践
cpp
void processFile(const string& filename) {
ifstream file(filename);
if (!file) {
cerr << "错误:无法打开文件 " << filename << endl;
perror("原因"); // 打印系统错误信息
return;
}
try {
// 文件处理代码
string line;
while (getline(file, line)) {
// 处理每一行
}
if (file.bad()) {
throw runtime_error("读取文件时发生严重错误");
}
if (file.fail() && !file.eof()) {
throw runtime_error("文件格式错误");
}
}
catch (const exception& e) {
cerr << "错误: " << e.what() << endl;
file.close();
return;
}
file.close();
}
九、性能考虑
-
缓冲区大小:默认缓冲区大小可能不适合大文件操作,可以自定义:
cppchar buffer[8192]; // 8KB缓冲区 ifstream file; file.rdbuf()->pubsetbuf(buffer, sizeof(buffer)); file.open("largefile.dat");
-
二进制 vs 文本:二进制操作通常比文本操作更快,特别是对于大量数据。
-
内存映射文件:对于极大文件,考虑使用操作系统特定的内存映射文件API。
十、C++17 文件系统库
C++17 引入了 <filesystem>
库,提供了更高级的文件操作:
cpp
#include <filesystem>
namespace fs = std::filesystem;
// 检查文件是否存在
if (fs::exists("test.txt")) {
// 获取文件大小
auto size = fs::file_size("test.txt");
// 复制文件
fs::copy("source.txt", "destination.txt");
// 删除文件
fs::remove("old_file.txt");
// 遍历目录
for (auto& entry : fs::directory_iterator(".")) {
cout << entry.path() << endl;
}
}
通过掌握这些文件操作技术,您可以在C++程序中高效地处理各种文件I/O任务。