std::ofstream
std::ofstream 是 C++ 标准库中用于文件输出的流类,它继承自 std::ostream,专门用于将数据写入文件。
基本头文件和命名空间
cpp
#include <fstream> // 主要头文件
#include <iostream> // 可选,用于 std::cout 等
#include <string>
using namespace std; // 为了简化示例
基本用法
1. 创建和打开文件
cpp
#include <fstream>
// 方法1: 先创建对象,再打开文件
std::ofstream outFile;
outFile.open("example.txt");
// 方法2: 创建时直接打开文件(推荐)
std::ofstream outFile("example.txt");
// 检查文件是否成功打开
if (!outFile.is_open()) {
std::cerr << "无法打开文件!" << std::endl;
return -1;
}
2. 写入数据的基本操作
cpp
#include <fstream>
#include <string>
int main() {
std::ofstream file("output.txt");
if (!file) { // 重载了bool运算符,可以直接检查
std::cerr << "文件打开失败" << std::endl;
return 1;
}
// 写入各种类型的数据
file << "Hello, World!" << std::endl; // 字符串
file << 42 << std::endl; // 整数
file << 3.14159 << std::endl; // 浮点数
file << true << std::endl; // 布尔值
// 使用变量
std::string name = "Alice";
int age = 25;
file << "姓名: " << name << ", 年龄: " << age << std::endl;
return 0;
}
文件打开模式
std::ofstream 支持多种打开模式,可以通过位或组合使用:
cpp
#include <fstream>
int main() {
// 不同打开模式的示例
// 截断模式(默认):如果文件存在,清空内容
std::ofstream file1("test1.txt", std::ios::out | std::ios::trunc);
// 追加模式:在文件末尾添加内容
std::ofstream file2("test2.txt", std::ios::out | std::ios::app);
// 二进制模式:以二进制方式写入
std::ofstream file3("test3.bin", std::ios::out | std::ios::binary);
// 同时支持读写(虽然叫ofstream,但可以打开为读写)
std::fstream file4("test4.txt", std::ios::in | std::ios::out);
return 0;
}
打开模式详解
| 模式标志 | 描述 |
|---|---|
std::ios::out |
输出模式(默认) |
std::ios::app |
追加模式,所有写入都加到文件末尾 |
std::ios::trunc |
截断模式,如果文件存在则清空 |
std::ios::binary |
二进制模式 |
std::ios::ate |
打开后定位到文件末尾 |
高级文件操作
1. 二进制文件操作
cpp
#include <fstream>
#include <vector>
struct Person {
char name[50];
int age;
double salary;
};
void binaryFileExample() {
// 写入二进制数据
std::ofstream outFile("data.bin", std::ios::binary);
Person p = {"John Doe", 30, 50000.0};
outFile.write(reinterpret_cast<char*>(&p), sizeof(Person));
// 写入多个对象
std::vector<Person> people = {
{"Alice", 25, 45000.0},
{"Bob", 35, 60000.0}
};
for (const auto& person : people) {
outFile.write(reinterpret_cast<const char*>(&person), sizeof(Person));
}
outFile.close();
}
2. 文件状态检查
cpp
#include <fstream>
void fileStatusExample() {
std::ofstream file("status.txt");
// 检查文件状态
if (file.is_open()) {
std::cout << "文件已成功打开" << std::endl;
}
if (file.good()) {
std::cout << "流状态良好" << std::endl;
}
// 写入后检查
file << "测试数据";
if (file.fail()) {
std::cerr << "写入失败" << std::endl;
}
if (file.bad()) {
std::cerr << "发生严重错误" << std::endl;
}
// 清除错误状态
file.clear();
}
3. 文件定位
cpp
#include <fstream>
void filePositioning() {
std::ofstream file("position.txt");
file << "Hello World";
// 获取当前写入位置
std::streampos pos = file.tellp();
std::cout << "当前位置: " << pos << std::endl;
// 移动到特定位置
file.seekp(6); // 移动到第6个字符位置
file << "C++"; // 覆盖 "World" 的一部分
// 从文件开头移动
file.seekp(0, std::ios::beg);
// 从当前位置移动
file.seekp(5, std::ios::cur);
// 从文件末尾移动
file.seekp(-3, std::ios::end);
}
实际应用示例
1. 日志系统
cpp
#include <fstream>
#include <chrono>
#include <iomanip>
class Logger {
private:
std::ofstream logFile;
public:
Logger(const std::string& filename) : logFile(filename, std::ios::app) {
if (!logFile.is_open()) {
throw std::runtime_error("无法打开日志文件");
}
}
void log(const std::string& message) {
auto now = std::chrono::system_clock::now();
auto time = std::chrono::system_clock::to_time_t(now);
logFile << "[" << std::put_time(std::localtime(&time), "%Y-%m-%d %H:%M:%S")
<< "] " << message << std::endl;
}
~Logger() {
if (logFile.is_open()) {
logFile.close();
}
}
};
// 使用示例
void useLogger() {
Logger logger("app.log");
logger.log("应用程序启动");
logger.log("执行某些操作");
logger.log("应用程序关闭");
}
2. 配置文件的写入
cpp
#include <fstream>
#include <map>
class ConfigWriter {
private:
std::string filename;
public:
ConfigWriter(const std::string& fname) : filename(fname) {}
bool writeConfig(const std::map<std::string, std::string>& config) {
std::ofstream file(filename);
if (!file.is_open()) {
return false;
}
file << "# 应用程序配置" << std::endl;
file << "# 自动生成,请勿手动修改" << std::endl << std::endl;
for (const auto& [key, value] : config) {
file << key << " = " << value << std::endl;
}
return true;
}
};
// 使用示例
void writeConfigExample() {
std::map<std::string, std::string> config = {
{"server.host", "localhost"},
{"server.port", "8080"},
{"database.url", "mysql://localhost:3306/mydb"},
{"log.level", "info"}
};
ConfigWriter writer("config.ini");
if (writer.writeConfig(config)) {
std::cout << "配置写入成功" << std::endl;
} else {
std::cerr << "配置写入失败" << std::endl;
}
}
3. 数据导出为CSV
cpp
#include <fstream>
#include <vector>
struct Employee {
std::string name;
std::string department;
double salary;
};
class CSVExporter {
public:
static bool exportToCSV(const std::vector<Employee>& employees,
const std::string& filename) {
std::ofstream file(filename);
if (!file.is_open()) {
return false;
}
// 写入表头
file << "姓名,部门,薪资" << std::endl;
// 写入数据
for (const auto& emp : employees) {
file << emp.name << ","
<< emp.department << ","
<< emp.salary << std::endl;
}
return true;
}
};
// 使用示例
void exportData() {
std::vector<Employee> employees = {
{"张三", "技术部", 15000.0},
{"李四", "销售部", 12000.0},
{"王五", "市场部", 13000.0}
};
if (CSVExporter::exportToCSV(employees, "employees.csv")) {
std::cout << "数据导出成功" << std::endl;
}
}
错误处理和最佳实践
1. 异常处理
cpp
#include <fstream>
#include <stdexcept>
void safeFileWriting() {
std::ofstream file;
// 启用异常
file.exceptions(std::ofstream::failbit | std::ofstream::badbit);
try {
file.open("important_data.txt");
file << "非常重要的数据..." << std::endl;
file.close();
}
catch (const std::ofstream::failure& e) {
std::cerr << "文件操作异常: " << e.what() << std::endl;
}
catch (const std::exception& e) {
std::cerr << "一般异常: " << e.what() << std::endl;
}
}
2. RAII 模式的应用
cpp
#include <fstream>
#include <memory>
// 使用智能指针管理文件资源
void smartFileWriting() {
auto file = std::make_unique<std::ofstream>("data.txt");
if (*file) {
*file << "使用智能指针管理文件" << std::endl;
// 不需要手动调用 close(),析构时会自动调用
}
}
// 或者使用作用域确保文件正确关闭
void scopeBasedFileWriting() {
{
std::ofstream file("scoped.txt");
file << "在作用域内使用文件" << std::endl;
} // 文件在这里自动关闭
// 文件已经关闭,可以安全进行其他操作
}
与 std::string 和 std::string_view 的配合
cpp
#include <fstream>
#include <string>
#include <string_view>
void stringIntegration() {
std::ofstream file("string_example.txt");
std::string data = "这是一个字符串";
std::string_view view = "这是一个字符串视图";
// 直接写入 string 和 string_view
file << "string: " << data << std::endl;
file << "string_view: " << view << std::endl;
// 从文件构建字符串
std::string filename = "config.txt";
std::ofstream configFile(filename);
configFile << "配置内容" << std::endl;
}
性能考虑
cpp
#include <fstream>
#include <chrono>
void performanceExample() {
std::ofstream file("large_data.txt");
// 不好的做法:频繁的格式化输出
auto start1 = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 10000; ++i) {
file << "数据: " << i << std::endl; // 多次格式化
}
auto end1 = std::chrono::high_resolution_clock::now();
// 更好的做法:批量写入
file.seekp(0); // 回到文件开头重新写
std::stringstream buffer;
auto start2 = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 10000; ++i) {
buffer << "数据: " << i << "\n";
}
file << buffer.str(); // 一次性写入
auto end2 = std::chrono::high_resolution_clock::now();
// 比较两种方法的时间
}
总结
std::ofstream 的主要特点:
- 专门用于文件输出操作
- 继承自 std::ostream,支持所有流操作
- 支持文本和二进制模式
- 自动管理文件打开和关闭(RAII)
- 支持文件定位和状态检查
最佳实践:
- 总是检查文件是否成功打开
- 使用 RAII 模式管理文件资源
- 对于大量数据,考虑批量写入以提高性能
- 根据需求选择合适的打开模式
- 在异常情况下确保文件正确关闭
适用场景:
- 日志记录
- 数据导出
- 配置文件生成
- 数据持久化存储
- 报告生成
std::ofstream 是 C++ 文件输出操作的核心工具,正确使用可以高效、安全地进行文件写入操作。