std_ofstream

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)
  • 支持文件定位和状态检查

最佳实践:

  1. 总是检查文件是否成功打开
  2. 使用 RAII 模式管理文件资源
  3. 对于大量数据,考虑批量写入以提高性能
  4. 根据需求选择合适的打开模式
  5. 在异常情况下确保文件正确关闭

适用场景:

  • 日志记录
  • 数据导出
  • 配置文件生成
  • 数据持久化存储
  • 报告生成

std::ofstream 是 C++ 文件输出操作的核心工具,正确使用可以高效、安全地进行文件写入操作。

相关推荐
草莓熊Lotso1 小时前
红黑树从入门到进阶:4 条规则如何筑牢 O (logN) 效率根基?
服务器·开发语言·c++·人工智能·经验分享·笔记·后端
啊董dong1 小时前
课后作业-2025年11月23号作业
数据结构·c++·算法·深度优先·noi
带鱼吃猫1 小时前
Linux系统:策略模式实现自定义日志功能
linux·c++
zzzsde2 小时前
【C++】C++11(1):右值引用和移动语义
开发语言·c++·算法
学困昇2 小时前
C++11中的包装器
开发语言·数据结构·c++·c++11
雪域迷影3 小时前
C++中编写UT单元测试用例时如何mock非虚函数?
开发语言·c++·测试用例·gmock·cpp-stub开源项目
是小胡嘛7 小时前
C++之Any类的模拟实现
linux·开发语言·c++
Want59510 小时前
C/C++跳动的爱心①
c语言·开发语言·c++
lingggggaaaa10 小时前
免杀对抗——C2远控篇&C&C++&DLL注入&过内存核晶&镂空新增&白加黑链&签名程序劫持
c语言·c++·学习·安全·网络安全·免杀对抗