C++ 之文件处理从入门到精通

41 阅读9分钟

目录

  1. 基础概念
  2. 文件流基础
  3. 文件读写操作
  4. 文件定位和状态
  5. 二进制文件处理
  6. 高级文件操作
  7. 实用工具类
  8. 错误处理

基础概念

文件类型

  • 文本文件:以ASCII码形式存储数据
  • 二进制文件:以二进制形式存储数据

文件模式

// ios::in     - 读取模式
// ios::out    - 写入模式
// ios::app    - 追加模式
// ios::ate    - 打开后定位到文件末尾
// ios::trunc  - 截断文件(清空内容)
// ios::binary - 二进制模式

文件流基础

三种文件流类

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main() {
    // 1. ofstream - 输出文件流(写文件)
    ofstream outFile;
    
    // 2. ifstream - 输入文件流(读文件)
    ifstream inFile;
    
    // 3. fstream - 文件流(读写文件)
    fstream file;
    
    return 0;
}

文件读写操作

1. 文本文件写入

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

using namespace std;

// 基础写入操作
void basicWrite() {
    ofstream file("example.txt");
    
    if (file.is_open()) {
        file << "Hello, World!" << endl;
        file << "This is a test file." << endl;
        file << "Number: " << 42 << endl;
        file.close();
        cout << "文件写入成功!" << endl;
    } else {
        cout << "无法打开文件!" << endl;
    }
}

// 向量数据写入
void vectorWrite() {
    vector<string> data = {
        "第一行数据",
        "第二行数据",
        "第三行数据"
    };
    
    ofstream file("vector_data.txt");
    if (file.is_open()) {
        for (const auto& line : data) {
            file << line << endl;
        }
        file.close();
        cout << "向量数据写入完成!" << endl;
    }
}

// 追加模式写入
void appendWrite() {
    ofstream file("example.txt", ios::app);
    if (file.is_open()) {
        file << "追加的内容" << endl;
        file.close();
        cout << "追加写入完成!" << endl;
    }
}

2. 文本文件读取

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

using namespace std;

// 逐行读取
void readLineByLine() {
    ifstream file("example.txt");
    string line;
    
    if (file.is_open()) {
        cout << "文件内容:" << endl;
        while (getline(file, line)) {
            cout << line << endl;
        }
        file.close();
    } else {
        cout << "无法打开文件!" << endl;
    }
}

// 逐词读取
void readWordByWord() {
    ifstream file("example.txt");
    string word;
    
    if (file.is_open()) {
        cout << "逐词读取:" << endl;
        while (file >> word) {
            cout << word << " ";
        }
        cout << endl;
        file.close();
    }
}

// 全部读取到字符串
void readAll() {
    ifstream file("example.txt");
    string content((istreambuf_iterator<char>(file)),
                   istreambuf_iterator<char>());
    
    if (!content.empty()) {
        cout << "全部内容:" << endl;
        cout << content << endl;
    }
    file.close();
}

// 读取到向量
vector<string> readToVector() {
    vector<string> lines;
    ifstream file("example.txt");
    string line;
    
    while (getline(file, line)) {
        lines.push_back(line);
    }
    file.close();
    return lines;
}

文件定位和状态

文件指针操作

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

void filePositionDemo() {
    fstream file("position_demo.txt", ios::in | ios::out | ios::trunc);
    
    if (file.is_open()) {
        // 写入初始数据
        file << "0123456789" << endl;
        file << "abcdefghij" << endl;
        
        // 获取当前位置
        streampos currentPos = file.tellp();
        cout << "当前位置:" << currentPos << endl;
        
        // 移动到文件开头
        file.seekp(0, ios::beg);
        file << "ABC";
        
        // 移动到文件末尾
        file.seekp(0, ios::end);
        file << "END";
        
        // 移动到相对位置
        file.seekp(-3, ios::cur);
        file << "XYZ";
        
        file.close();
    }
}

// 检查文件状态
void checkFileStatus() {
    ifstream file("test.txt");
    
    cout << "文件状态检查:" << endl;
    cout << "is_open(): " << file.is_open() << endl;
    cout << "good(): " << file.good() << endl;
    cout << "eof(): " << file.eof() << endl;
    cout << "fail(): " << file.fail() << endl;
    cout << "bad(): " << file.bad() << endl;
    
    file.close();
}

二进制文件处理

二进制文件读写

#include <iostream>
#include <fstream>
#include <vector>
#include <cstring>

using namespace std;

// 学生结构体
struct Student {
    int id;
    char name[50];
    double score;
    
    Student() : id(0), score(0.0) {
        strcpy(name, "");
    }
    
    Student(int i, const char* n, double s) : id(i), score(s) {
        strncpy(name, n, 49);
        name[49] = '\0';
    }
};

// 写入二进制文件
void writeBinaryFile() {
    vector<Student> students = {
        Student(1, "张三", 85.5),
        Student(2, "李四", 92.0),
        Student(3, "王五", 78.5)
    };
    
    ofstream file("students.dat", ios::binary);
    if (file.is_open()) {
        for (const auto& student : students) {
            file.write(reinterpret_cast<const char*>(&student), sizeof(Student));
        }
        file.close();
        cout << "二进制文件写入完成!" << endl;
    }
}

// 读取二进制文件
void readBinaryFile() {
    ifstream file("students.dat", ios::binary);
    if (file.is_open()) {
        Student student;
        cout << "学生信息:" << endl;
        while (file.read(reinterpret_cast<char*>(&student), sizeof(Student))) {
            cout << "ID: " << student.id 
                 << ", 姓名: " << student.name 
                 << ", 分数: " << student.score << endl;
        }
        file.close();
    }
}

// 随机访问二进制文件
void randomAccessBinary() {
    fstream file("students.dat", ios::in | ios::out | ios::binary);
    if (file.is_open()) {
        Student student;
        
        // 读取第二个学生
        file.seekg(sizeof(Student), ios::beg);
        file.read(reinterpret_cast<char*>(&student), sizeof(Student));
        cout << "第二个学生: " << student.name << endl;
        
        // 修改第三个学生
        Student newStudent(3, "赵六", 95.0);
        file.seekp(2 * sizeof(Student), ios::beg);
        file.write(reinterpret_cast<const char*>(&newStudent), sizeof(Student));
        
        file.close();
    }
}

高级文件操作

CSV文件处理

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>

using namespace std;

class CSVReader {
private:
    string filename;
    
public:
    CSVReader(const string& fname) : filename(fname) {}
    
    vector<vector<string>> readCSV() {
        vector<vector<string>> data;
        ifstream file(filename);
        
        if (!file.is_open()) {
            cerr << "无法打开文件: " << filename << endl;
            return data;
        }
        
        string line;
        while (getline(file, line)) {
            vector<string> row;
            stringstream ss(line);
            string cell;
            
            while (getline(ss, cell, ',')) {
                row.push_back(cell);
            }
            data.push_back(row);
        }
        
        file.close();
        return data;
    }
    
    void writeCSV(const vector<vector<string>>& data) {
        ofstream file(filename);
        
        if (!file.is_open()) {
            cerr << "无法创建文件: " << filename << endl;
            return;
        }
        
        for (const auto& row : data) {
            for (size_t i = 0; i < row.size(); ++i) {
                file << row[i];
                if (i < row.size() - 1) {
                    file << ",";
                }
            }
            file << endl;
        }
        
        file.close();
    }
};

// 使用示例
void csvExample() {
    // 创建示例数据
    vector<vector<string>> data = {
        {"姓名", "年龄", "城市"},
        {"张三", "25", "北京"},
        {"李四", "30", "上海"},
        {"王五", "28", "广州"}
    };
    
    // 写入CSV文件
    CSVReader writer("data.csv");
    writer.writeCSV(data);
    
    // 读取CSV文件
    CSVReader reader("data.csv");
    auto readData = reader.readCSV();
    
    cout << "读取的CSV数据:" << endl;
    for (const auto& row : readData) {
        for (const auto& cell : row) {
            cout << cell << "\t";
        }
        cout << endl;
    }
}

配置文件处理

#include <iostream>
#include <fstream>
#include <map>
#include <string>
#include <algorithm>

using namespace std;

class ConfigFile {
private:
    map<string, string> config;
    string filename;
    
    // 去除字符串两端空格
    string trim(const string& str) {
        size_t first = str.find_first_not_of(' ');
        if (first == string::npos) return "";
        size_t last = str.find_last_not_of(' ');
        return str.substr(first, (last - first + 1));
    }
    
public:
    ConfigFile(const string& fname) : filename(fname) {
        load();
    }
    
    // 加载配置文件
    void load() {
        ifstream file(filename);
        if (!file.is_open()) return;
        
        string line;
        while (getline(file, line)) {
            // 跳过注释和空行
            if (line.empty() || line[0] == '#' || line[0] == ';') continue;
            
            // 查找等号
            size_t pos = line.find('=');
            if (pos != string::npos) {
                string key = trim(line.substr(0, pos));
                string value = trim(line.substr(pos + 1));
                config[key] = value;
            }
        }
        file.close();
    }
    
    // 保存配置文件
    void save() {
        ofstream file(filename);
        if (!file.is_open()) return;
        
        for (const auto& pair : config) {
            file << pair.first << " = " << pair.second << endl;
        }
        file.close();
    }
    
    // 设置配置项
    void set(const string& key, const string& value) {
        config[key] = value;
    }
    
    // 获取配置项
    string get(const string& key, const string& defaultValue = "") {
        auto it = config.find(key);
        return (it != config.end()) ? it->second : defaultValue;
    }
    
    // 获取整数配置
    int getInt(const string& key, int defaultValue = 0) {
        string value = get(key);
        if (!value.empty()) {
            try {
                return stoi(value);
            } catch (...) {
                return defaultValue;
            }
        }
        return defaultValue;
    }
    
    // 获取浮点数配置
    double getDouble(const string& key, double defaultValue = 0.0) {
        string value = get(key);
        if (!value.empty()) {
            try {
                return stod(value);
            } catch (...) {
                return defaultValue;
            }
        }
        return defaultValue;
    }
    
    // 检查键是否存在
    bool exists(const string& key) {
        return config.find(key) != config.end();
    }
    
    // 删除配置项
    void remove(const string& key) {
        config.erase(key);
    }
    
    // 显示所有配置
    void display() {
        cout << "配置文件内容:" << endl;
        for (const auto& pair : config) {
            cout << pair.first << " = " << pair.second << endl;
        }
    }
};

// 使用示例
void configFileExample() {
    // 创建配置文件
    {
        ofstream file("config.ini");
        file << "# 配置文件示例\n";
        file << "server_ip = 192.168.1.100\n";
        file << "port = 8080\n";
        file << "timeout = 30.5\n";
        file << "debug = true\n";
        file.close();
    }
    
    // 使用配置文件类
    ConfigFile config("config.ini");
    
    cout << "服务器IP: " << config.get("server_ip") << endl;
    cout << "端口: " << config.getInt("port") << endl;
    cout << "超时时间: " << config.getDouble("timeout") << endl;
    cout << "调试模式: " << config.get("debug") << endl;
    
    // 修改配置
    config.set("server_ip", "192.168.1.200");
    config.set("max_connections", "100");
    
    // 保存配置
    config.save();
    
    // 显示所有配置
    config.display();
}

实用工具类

文件操作工具类

#include <iostream>
#include <fstream>
#include <filesystem>
#include <vector>
#include <string>

using namespace std;
namespace fs = std::filesystem;

class FileUtils {
public:
    // 检查文件是否存在
    static bool fileExists(const string& filename) {
        return fs::exists(filename);
    }
    
    // 获取文件大小
    static size_t getFileSize(const string& filename) {
        if (!fileExists(filename)) return 0;
        return fs::file_size(filename);
    }
    
    // 复制文件
    static bool copyFile(const string& source, const string& destination) {
        try {
            fs::copy_file(source, destination, fs::copy_options::overwrite_existing);
            return true;
        } catch (const fs::filesystem_error& ex) {
            cerr << "复制文件失败: " << ex.what() << endl;
            return false;
        }
    }
    
    // 移动文件
    static bool moveFile(const string& source, const string& destination) {
        try {
            fs::rename(source, destination);
            return true;
        } catch (const fs::filesystem_error& ex) {
            cerr << "移动文件失败: " << ex.what() << endl;
            return false;
        }
    }
    
    // 删除文件
    static bool deleteFile(const string& filename) {
        try {
            return fs::remove(filename);
        } catch (const fs::filesystem_error& ex) {
            cerr << "删除文件失败: " << ex.what() << endl;
            return false;
        }
    }
    
    // 创建目录
    static bool createDirectory(const string& path) {
        try {
            return fs::create_directories(path);
        } catch (const fs::filesystem_error& ex) {
            cerr << "创建目录失败: " << ex.what() << endl;
            return false;
        }
    }
    
    // 列出目录中的文件
    static vector<string> listFiles(const string& directory) {
        vector<string> files;
        try {
            for (const auto& entry : fs::directory_iterator(directory)) {
                if (entry.is_regular_file()) {
                    files.push_back(entry.path().string());
                }
            }
        } catch (const fs::filesystem_error& ex) {
            cerr << "列出文件失败: " << ex.what() << endl;
        }
        return files;
    }
    
    // 读取整个文件到字符串
    static string readFileToString(const string& filename) {
        ifstream file(filename, ios::binary);
        if (!file.is_open()) return "";
        
        string content((istreambuf_iterator<char>(file)),
                       istreambuf_iterator<char>());
        file.close();
        return content;
    }
    
    // 将字符串写入文件
    static bool writeStringToFile(const string& filename, const string& content) {
        ofstream file(filename, ios::binary);
        if (!file.is_open()) return false;
        
        file << content;
        file.close();
        return true;
    }
    
    // 获取文件扩展名
    static string getFileExtension(const string& filename) {
        size_t pos = filename.find_last_of('.');
        if (pos != string::npos) {
            return filename.substr(pos + 1);
        }
        return "";
    }
    
    // 获取文件名(不含路径)
    static string getFileName(const string& filepath) {
        size_t pos = filepath.find_last_of("/\\");
        if (pos != string::npos) {
            return filepath.substr(pos + 1);
        }
        return filepath;
    }
};

// 使用示例
void fileUtilsExample() {
    // 创建测试文件
    ofstream testFile("test.txt");
    testFile << "这是一个测试文件的内容。" << endl;
    testFile.close();
    
    // 测试文件工具类
    cout << "文件存在: " << FileUtils::fileExists("test.txt") << endl;
    cout << "文件大小: " << FileUtils::getFileSize("test.txt") << " 字节" << endl;
    cout << "文件扩展名: " << FileUtils::getFileExtension("test.txt") << endl;
    cout << "文件名: " << FileUtils::getFileName("/path/to/test.txt") << endl;
    
    // 复制文件
    FileUtils::copyFile("test.txt", "test_copy.txt");
    cout << "文件复制完成" << endl;
    
    // 读取文件内容
    string content = FileUtils::readFileToString("test.txt");
    cout << "文件内容: " << content << endl;
    
    // 清理测试文件
    FileUtils::deleteFile("test.txt");
    FileUtils::deleteFile("test_copy.txt");
}

错误处理

异常安全的文件操作

#include <iostream>
#include <fstream>
#include <string>
#include <stdexcept>

using namespace std;

class SafeFileHandler {
private:
    fstream file;
    string filename;
    
public:
    SafeFileHandler(const string& fname) : filename(fname) {}
    
    ~SafeFileHandler() {
        if (file.is_open()) {
            file.close();
        }
    }
    
    // 安全打开文件
    bool open(ios::openmode mode) {
        try {
            file.open(filename, mode);
            if (!file.is_open()) {
                throw runtime_error("无法打开文件: " + filename);
            }
            return true;
        } catch (const exception& ex) {
            cerr << "文件打开错误: " << ex.what() << endl;
            return false;
        }
    }
    
    // 安全写入
    bool write(const string& data) {
        try {
            if (!file.is_open()) {
                throw runtime_error("文件未打开");
            }
            file << data;
            if (file.fail()) {
                throw runtime_error("写入失败");
            }
            return true;
        } catch (const exception& ex) {
            cerr << "写入错误: " << ex.what() << endl;
            return false;
        }
    }
    
    // 安全读取
    bool read(string& data) {
        try {
            if (!file.is_open()) {
                throw runtime_error("文件未打开");
            }
            data.assign((istreambuf_iterator<char>(file)),
                       istreambuf_iterator<char>());
            if (file.bad()) {
                throw runtime_error("读取失败");
            }
            return true;
        } catch (const exception& ex) {
            cerr << "读取错误: " << ex.what() << endl;
            return false;
        }
    }
};

// RAII文件操作示例
class FileRAII {
private:
    ofstream file;
    
public:
    FileRAII(const string& filename) : file(filename) {
        if (!file.is_open()) {
            throw runtime_error("无法创建文件: " + filename);
        }
    }
    
    ~FileRAII() {
        if (file.is_open()) {
            file.close();
        }
    }
    
    void write(const string& data) {
        file << data;
    }
};

// 使用示例
void errorHandlingExample() {
    try {
        // 使用RAII确保文件正确关闭
        {
            FileRAII file("safe_example.txt");
            file.write("安全的文件操作示例");
        } // 文件在这里自动关闭
        
        // 使用安全文件处理器
        SafeFileHandler handler("safe_example.txt");
        if (handler.open(ios::in)) {
            string content;
            if (handler.read(content)) {
                cout << "读取内容: " << content << endl;
            }
        }
        
    } catch (const exception& ex) {
        cerr << "异常: " << ex.what() << endl;
    }
}

完整示例程序

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <chrono>

using namespace std;

// 日志文件类
class Logger {
private:
    ofstream logFile;
    string filename;
    
public:
    Logger(const string& fname) : filename(fname) {
        logFile.open(filename, ios::app);
        if (!logFile.is_open()) {
            cerr << "无法打开日志文件: " << filename << endl;
        }
    }
    
    ~Logger() {
        if (logFile.is_open()) {
            logFile.close();
        }
    }
    
    void log(const string& message) {
        auto now = chrono::system_clock::now();
        auto time_t = chrono::system_clock::to_time_t(now);
        
        if (logFile.is_open()) {
            logFile << ctime(&time_t) << ": " << message << endl;
        }
    }
};

// 主程序示例
int main() {
    cout << "=== C++ 文件处理示例 ===" << endl;
    
    // 1. 基础文件操作
    basicWrite();
    readLineByLine();
    
    // 2. 二进制文件操作
    writeBinaryFile();
    readBinaryFile();
    
    // 3. CSV文件处理
    csvExample();
    
    // 4. 配置文件处理
    configFileExample();
    
    // 5. 文件工具类
    fileUtilsExample();
    
    // 6. 错误处理
    errorHandlingExample();
    
    // 7. 日志记录示例
    Logger logger("app.log");
    logger.log("应用程序启动");
    logger.log("执行文件操作");
    logger.log("应用程序关闭");
    
    cout << "所有示例执行完成!" << endl;
    return 0;
}

总结

关键要点

  1. 选择合适的流类

    • ofstream 用于写文件
    • ifstream 用于读文件
    • fstream 用于读写文件
  2. 正确管理资源

    • 使用 RAII 原则
    • 确保文件正确关闭
    • 处理异常情况
  3. 模式选择

    • 文本模式 vs 二进制模式
    • 读写模式组合
    • 追加 vs 截断
  4. 性能考虑

    • 缓冲区使用
    • 批量操作
    • 避免频繁的文件打开/关闭

最佳实践

  1. 始终检查文件操作是否成功
  2. 使用智能指针或RAII管理文件资源
  3. 合理使用异常处理
  4. 选择合适的文件访问模式
  5. 注意跨平台兼容性

这个教程涵盖了C++文件处理的各个方面,从基础到高级,提供了丰富的代码示例和实用工具类,可以帮助你掌握C++文件处理的各种技巧。