C++文件操作详细讲解(完善版)

一、C++风格文件操作(iostream库)

1.1 基础知识

#include <fstream>   // 文件流
#include <iostream>  // 标准输入输出
#include <string>
#include <sstream>   // 字符串流
 
// 三个主要类:
// ifstream - 输入文件流(读取)
// ofstream - 输出文件流(写入)
// fstream  - 输入输出文件流(读写)

流的继承关系:

  • ios 是所有流的基类,定义基本的流操作
  • istream 继承自 ios,提供输入操作
  • ostream 继承自 ios,提供输出操作
  • iostream 继承自 istreamostream
  • ifstream 继承自 istream
  • ofstream 继承自 ostream
  • fstream 继承自 iostream

1.2 文件打开模式详解

// 基础模式
ios::in      // 读取模式(默认)
ios::out     // 写入模式(默认会清空文件)
ios::app     // 追加模式(指针始终在文件末尾)
ios::ate     // 打开文件后定位到文件末尾(但指针可以移动)
ios::trunc   // 打开文件时清空内容(与 out 一起使用)
ios::binary  // 二进制模式(不进行换行符转换)
 
// 模式组合示例
ios::in | ios::binary              // 二进制读取
ios::out | ios::binary             // 二进制写入
ios::in | ios::out                 // 读写模式
ios::out | ios::trunc              // 写入并清空(默认)
ios::in | ios::out | ios::binary   // 二进制读写
 
// 重要区别
// ios::ate - 打开后可移动指针,适合定位
// ios::app - 每次写入都在末尾,指针不可回退

模式选择指南:

模式文件存在文件不存在指针位置用途
in打开读取失败开头只读
out清空覆盖创建开头只写
app打开追加创建末尾日志/追加
ate打开创建末尾定位读取
in|out打开失败开头读写
out|trunc清空覆盖创建开头覆盖写入

1.3 文本文件操作详解

#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
 
// ========== 写入文本文件 ==========
void writeTextFile() {
    // 方法1:构造函数打开
    ofstream outFile("data.txt");
    
    // 方法2:open函数打开(可复用对象)
    // ofstream outFile;
    // outFile.open("data.txt", ios::out);
    
    // 方法3:创建并立即检查
    // ofstream outFile("data.txt");
    // if (!outFile) { ... }
    
    if (!outFile.is_open()) {
        cerr << "文件打开失败!" << endl;
        return;
    }
    
    // 基本写入
    outFile << "Hello, World!" << endl;
    outFile << "这是第二行" << endl;
    outFile << "数字: " << 123 << ", 浮点数: " << 3.14 << endl;
    
    // 关闭文件(析构函数会自动关闭,但建议显式关闭)
    outFile.close();
    
    // 检查是否成功关闭
    if (outFile.fail()) {
        cerr << "文件关闭失败" << endl;
    }
}
 
// ========== 读取文本文件 - 逐行读取(推荐) ==========
void readLineByLine() {
    ifstream inFile("data.txt");
    
    if (!inFile.is_open()) {
        cerr << "文件打开失败!" << endl;
        return;
    }
    
    string line;
    int lineNum = 0;
    
    // getline 是读取一行的标准方法
    while (getline(inFile, line)) {
        lineNum++;
        cout << "第 " << lineNum << " 行: " << line << endl;
        
        // 可以在这里处理每一行
        if (line.empty()) {
            cout << "  (空行)" << endl;
        }
    }
    
    cout << "共读取 " << lineNum << " 行" << endl;
    
    inFile.close();
}
 
// ========== 读取文本文件 - 按单词读取 ==========
void readWordByWord() {
    ifstream inFile("data.txt");
    
    if (!inFile) return;
    
    string word;
    int wordCount = 0;
    
    // 提取由空白符分隔的单词
    while (inFile >> word) {
        wordCount++;
        cout << "单词 " << wordCount << ": " << word << endl;
    }
    
    cout << "共读取 " << wordCount << " 个单词" << endl;
    inFile.close();
}
 
// ========== 读取文本文件 - 按字符读取 ==========
void readCharByChar() {
    ifstream inFile("data.txt");
    
    if (!inFile) return;
    
    char ch;
    int charCount = 0;
    
    // 逐字符读取(包括空白符和换行符)
    while (inFile.get(ch)) {
        charCount++;
        if (ch == '\n') {
            cout << "[换行]" << endl;
        } else if (ch == '\t') {
            cout << "[制表符]";
        } else {
            cout << ch;
        }
    }
    
    cout << "\n共读取 " << charCount << " 个字符" << endl;
    inFile.close();
}
 
// ========== 读取文本文件 - 一次性读入全部 ==========
void readEntireFile() {
    ifstream inFile("data.txt");
    
    if (!inFile) return;
    
    // 方法1:使用流迭代器
    string content((istreambuf_iterator<char>(inFile)),
                   istreambuf_iterator<char>());
    cout << "文件内容:" << endl << content << endl;
    cout << "文件大小:" << content.length() << " 字符" << endl;
    
    inFile.close();
}
 
// ========== 读取文本文件 - 逐行到字符串向量 ==========
void readAllLinesToVector() {
    ifstream inFile("data.txt");
    
    if (!inFile) return;
    
    vector<string> lines;
    string line;
    
    while (getline(inFile, line)) {
        lines.push_back(line);
    }
    
    cout << "总行数:" << lines.size() << endl;
    for (size_t i = 0; i < lines.size(); ++i) {
        cout << "行 " << (i + 1) << ": " << lines[i] << endl;
    }
    
    inFile.close();
}
 
// ========== 追加模式 ==========
void appendToFile() {
    // 使用 ios::app 模式
    ofstream outFile("data.txt", ios::app);
    
    if (!outFile.is_open()) {
        cerr << "打开失败" << endl;
        return;
    }
    
    outFile << "这是追加的内容" << endl;
    outFile << "再追加一行" << endl;
    
    outFile.close();
}
 
// ========== 检查文件是否存在 ==========
bool fileExists(const string& filename) {
    ifstream inFile(filename);
    return inFile.good();  // 如果文件存在且可打开,返回true
}
 
// ========== 计算文件行数 ==========
int countLines(const string& filename) {
    ifstream inFile(filename);
    if (!inFile) return -1;
    
    int count = 0;
    string line;
    
    while (getline(inFile, line)) {
        count++;
    }
    
    inFile.close();
    return count;
}
 
// ========== 带格式化的读写 ==========
void formattedReadWrite() {
    // 写入格式化数据
    ofstream outFile("data.txt");
    outFile << "姓名: " << "张三" << endl;
    outFile << "年龄: " << 25 << endl;
    outFile << "分数: " << 89.5f << endl;
    outFile.close();
    
    // 读取并解析
    ifstream inFile("data.txt");
    string name;
    int age;
    float score;
    
    string dummy;  // 用于读取前缀
    getline(inFile, dummy);  // 读整行
    
    // 使用字符串流解析
    stringstream ss(dummy);
    ss >> dummy >> name;  // 跳过"姓名:",读名字
    
    inFile.close();
}

1.4 二进制文件操作详解

#include <fstream>
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
 
// 定义结构体
struct Student {
    char name[50];
    int age;
    float score;
};
 
// ========== 写入二进制文件 ==========
void writeBinaryFile() {
    ofstream outFile("student.dat", ios::binary);
    
    if (!outFile) {
        cerr << "文件打开失败!" << endl;
        return;
    }
    
    // 单个结构体写入
    Student s1;
    strcpy(s1.name, "张三");
    s1.age = 20;
    s1.score = 95.5;
    
    // write(const char* buffer, streamsize size)
    // 必须转换为 char* 指针
    outFile.write(reinterpret_cast<char*>(&s1), sizeof(Student));
    
    // 批量写入多个结构体
    Student students[3] = {
        {"李四", 21, 88.0},
        {"王五", 19, 92.5},
        {"赵六", 22, 85.0}
    };
    
    outFile.write(reinterpret_cast<char*>(students), sizeof(students));
    
    // 写入基本类型数据
    int number = 12345;
    double value = 3.14159;
    
    outFile.write(reinterpret_cast<char*>(&number), sizeof(number));
    outFile.write(reinterpret_cast<char*>(&value), sizeof(value));
    
    outFile.close();
}
 
// ========== 读取二进制文件 ==========
void readBinaryFile() {
    ifstream inFile("student.dat", ios::binary);
    
    if (!inFile) {
        cerr << "文件打开失败!" << endl;
        return;
    }
    
    Student s;
    
    // read(char* buffer, streamsize size)
    // 返回 istream 对象,可以检查状态
    while (inFile.read(reinterpret_cast<char*>(&s), sizeof(Student))) {
        cout << "姓名: " << s.name << endl;
        cout << "年龄: " << s.age << endl;
        cout << "分数: " << s.score << endl;
        cout << "-------------------" << endl;
    }
    
    // 检查是否因错误而停止(而不是EOF)
    if (!inFile.eof()) {
        cerr << "读取过程中出现错误" << endl;
    }
    
    inFile.close();
}
 
// ========== 读取二进制数据到向量 ==========
void readBinaryToVector() {
    ifstream inFile("student.dat", ios::binary);
    if (!inFile) return;
    
    vector<Student> students;
    Student s;
    
    while (inFile.read(reinterpret_cast<char*>(&s), sizeof(Student))) {
        students.push_back(s);
    }
    
    cout << "读取了 " << students.size() << " 个学生记录" << endl;
    
    inFile.close();
}
 
// ========== 修改二进制文件中的数据 ==========
void modifyBinaryFile() {
    fstream file("student.dat", ios::in | ios::out | ios::binary);
    
    if (!file) return;
    
    Student s;
    int recordIndex = 0;  // 要修改的记录索引
    
    // 定位到指定记录
    file.seekg(recordIndex * sizeof(Student), ios::beg);
    
    // 读取记录
    file.read(reinterpret_cast<char*>(&s), sizeof(Student));
    
    // 修改数据
    s.score = 99.5f;
    
    // 回到同一位置
    file.seekp(recordIndex * sizeof(Student), ios::beg);
    
    // 写入修改后的数据
    file.write(reinterpret_cast<char*>(&s), sizeof(Student));
    
    file.close();
}
 
// ========== 比较文本和二进制文件大小 ==========
void compareFileSizes() {
    // 文本文件大小可能不等于字节数(因为换行符转换)
    // 二进制文件大小精确对应实际数据
    
    ifstream textFile("data.txt");
    ifstream binFile("data.dat", ios::binary);
    
    textFile.seekg(0, ios::end);
    binFile.seekg(0, ios::end);
    
    cout << "文本文件指针位置: " << textFile.tellg() << endl;
    cout << "二进制文件指针位置: " << binFile.tellg() << endl;
}

1.5 文件指针操作详解

#include <fstream>
#include <iostream>
using namespace std;
 
void filePointerOperations() {
    fstream file("data.txt", ios::in | ios::out | ios::binary);
    
    if (!file) return;
    
    // ========== 获取文件大小 ==========
    file.seekg(0, ios::end);
    streampos fileSize = file.tellg();
    cout << "文件大小: " << fileSize << " 字节" << endl;
    
    // ========== 输入流指针操作(读取指针) ==========
    
    // seekg(offset, whence) - 移动读取指针
    file.seekg(0, ios::beg);    // 移到文件开头
    file.seekg(10, ios::cur);   // 从当前位置向后移动10字节
    file.seekg(-5, ios::end);   // 从文件末尾向前移动5字节
    file.seekg(100);            // 直接定位到第100字节(默认从开头)
    
    // tellg() - 获取读取指针当前位置
    streampos pos = file.tellg();
    cout << "当前读取位置: " << pos << endl;
    
    // ========== 输出流指针操作(写入指针) ==========
    
    // seekp(offset, whence) - 移动写入指针
    file.seekp(0, ios::beg);
    file.seekp(0, ios::end);
    
    // tellp() - 获取写入指针当前位置
    streampos writePos = file.tellp();
    cout << "当前写入位置: " << writePos << endl;
    
    // ========== 注意事项 ==========
    
    // 对于 fstream,读写指针是独立的
    file.seekg(0, ios::beg);     // 移动读取指针
    file.seekp(10, ios::beg);    // 移动写入指针,不影响读取指针
    
    // 在读写交替时需要小心管理指针
    char buffer[100];
    file.read(buffer, 10);       // 从当前读指针位置读取
    file.write(buffer, 10);      // 从当前写指针位置写入
    
    file.close();
}
 
// ========== 从文件中间读取特定位置数据 ==========
void readFromPosition(const string& filename, long position, int size) {
    ifstream inFile(filename, ios::binary);
    if (!inFile) return;
    
    inFile.seekg(position);
    
    char* buffer = new char[size];
    inFile.read(buffer, size);
    
    cout << "读取 " << inFile.gcount() << " 字节" << endl;
    
    delete[] buffer;
    inFile.close();
}
 
// ========== 反向读取文件 ==========
void readFileReverse(const string& filename) {
    ifstream inFile(filename, ios::binary);
    if (!inFile) return;
    
    // 定位到文件末尾
    inFile.seekg(0, ios::end);
    streampos size = inFile.tellg();
    
    // 从末尾向前读取
    for (long i = size - 1; i >= 0; --i) {
        inFile.seekg(i);
        char ch;
        inFile.get(ch);
        cout << ch;
    }
    
    inFile.close();
}

1.6 错误处理和状态检查详解

#include <fstream>
#include <iostream>
using namespace std;
 
void errorHandling() {
    ifstream file("test.txt");
    
    // ========== 状态检查函数 ==========
    
    // good() - 所有状态位都正常(最严格)
    if (file.good()) {
        cout << "文件状态完全良好,可继续操作" << endl;
    }
    
    // eof() - 到达文件末尾
    if (file.eof()) {
        cout << "已到达文件末尾" << endl;
    }
    
    // fail() - 操作失败(如打开失败、格式错误)
    if (file.fail()) {
        cout << "读写操作失败(逻辑错误)" << endl;
    }
    
    // bad() - 严重错误(如硬件故障、流被破坏)
    if (file.bad()) {
        cout << "发生严重错误" << endl;
    }
    
    // operator bool() - 检查是否可用(推荐)
    if (file) {
        cout << "文件流处于良好状态" << endl;
    }
    
    if (!file) {
        cout << "文件流出错或未打开" << endl;
    }
    
    // ========== rdstate() - 获取完整状态 ==========
    ios::iostate state = file.rdstate();
    
    if (state == ios::goodbit) {
        cout << "完全正常" << endl;
    } else if (state & ios::failbit) {
        cout << "逻辑错误已设置" << endl;
    } else if (state & ios::badbit) {
        cout << "严重错误已设置" << endl;
    }
    
    // ========== setstate() - 设置状态 ==========
    file.setstate(ios::failbit);
    
    // ========== 清除错误状态 ==========
    file.clear();  // 清除所有错误标志,设置 goodbit
    
    // 清除特定标志
    file.clear(ios::goodbit);
    
    // ========== gcount() - 获取最后一次读取的字符数 ==========
    char buffer[100];
    file.read(buffer, 100);
    cout << "实际读取: " << file.gcount() << " 字节" << endl;
    
    // ========== 异常处理 ==========
    
    // 方法1:设置异常掩码
    file.exceptions(ifstream::failbit | ifstream::badbit);
    
    try {
        ifstream inFile("test.txt");
        string line;
        
        while (getline(inFile, line)) {
            cout << line << endl;
        }
        
        inFile.close();
    } catch (ifstream::failure& e) {
        cerr << "文件操作异常: " << e.what() << endl;
    }
    
    // ========== 手动检查模式 ==========
    ifstream file2("test.txt");
    
    while (true) {
        char buffer[256];
        file2.read(buffer, 256);
        
        if (file2.gcount() > 0) {
            // 处理读取的数据
        }
        
        if (file2.eof()) {
            break;  // 正常结束
        }
        
        if (file2.fail()) {
            cerr << "读取失败" << endl;
            break;
        }
    }
    
    file2.close();
}
 
// ========== 完整的文件读取示例 ==========
void robustFileRead(const string& filename) {
    ifstream file(filename);
    
    // 检查打开状态
    if (!file.is_open()) {
        cerr << "无法打开文件: " << filename << endl;
        return;
    }
    
    string line;
    int lineNum = 0;
    
    // 使用getline进行安全读取
    while (getline(file, line)) {
        lineNum++;
        cout << "行 " << lineNum << ": " << line << endl;
    }
    
    // 判断退出原因
    if (file.eof()) {
        cout << "成功读取到文件末尾" << endl;
    } else if (file.fail()) {
        cerr << "读取过程中出现逻辑错误" << endl;
    } else if (file.bad()) {
        cerr << "读取过程中出现严重错误" << endl;
    }
    
    file.close();
}

1.7 流状态标志详解

/*
ios::goodbit (0)    - 无错误
ios::badbit (1)     - 严重错误,流被破坏
ios::failbit (2)    - 操作失败
ios::eofbit (4)     - 到达文件末尾
 
状态转换规则:
1. 成功操作 -> goodbit
2. 逻辑错误 -> failbit (failbit会自动设置eofbit)
3. 读到EOF -> eofbit (不是错误状态)
4. 严重错误 -> badbit
*/
 
void stateFlags() {
    ifstream file("test.txt");
    
    // 检查特定标志
    if (file.rdstate() & ios::badbit) {
        cout << "流已损坏" << endl;
    }
    
    if (file.rdstate() & ios::failbit) {
        cout << "操作失败" << endl;
    }
    
    if (file.rdstate() & ios::eofbit) {
        cout << "已到达末尾" << endl;
    }
}

1.8 缓冲区管理

#include <fstream>
#include <iostream>
using namespace std;
 
void bufferManagement() {
    ofstream outFile("data.txt");
    
    // ========== flush() - 刷新缓冲区 ==========
    outFile << "第一行" << flush;  // 立即写入磁盘
    outFile << "第二行" << endl;   // endl 包含了 flush
    
    // 手动刷新
    outFile << "第三行";
    outFile.flush();  // 等同于 << flush
    
    // ========== 禁用同步 - 提高性能 ==========
    cin.tie(nullptr);  // 断开 cin 和 cout 的绑定
    ios::sync_with_stdio(false);  // 加快I/O速度
    
    // ========== 自定义缓冲区大小 ==========
    char buffer[1024];
    outFile.rdbuf()->pubsetbuf(buffer, sizeof(buffer));
    
    // ========== 获取缓冲区信息 ==========
    streamsize bufSize = outFile.rdbuf()->in_avail();
    cout << "缓冲区可用字节数: " << bufSize << endl;
    
    outFile.close();
}

二、C风格文件操作(stdio.h)

2.1 基本文件操作

#include <cstdio>
#include <cstring>
#include <cerrno>
using namespace std;
 
void basicFileOperations() {
    // fopen(文件名, 模式) - 返回 FILE* 指针
    FILE* fp = fopen("data.txt", "w");
    
    if (fp == NULL) {
        perror("文件打开失败");
        return;
    }
    
    // 写入数据
    fprintf(fp, "Hello, World!\n");
    fprintf(fp, "数字: %d\n", 123);
    
    // 关闭文件
    int result = fclose(fp);
    
    if (result == EOF) {
        perror("文件关闭失败");
    }
    
    // ========== fopen_s (Windows特有) ==========
    // FILE* fp_safe;
    // errno_t err = fopen_s(&fp_safe, "data.txt", "w");
    // if (err == 0) { ... }
}

2.2 文件打开模式详解(C风格)

/*
"r"   - 只读,文件必须存在
"w"   - 只写,创建新文件或覆盖已有文件
"a"   - 追加,在文件末尾写入,不存在则创建
"r+"  - 读写,文件必须存在
"w+"  - 读写,创建新文件或覆盖已有文件
"a+"  - 读写,在文件末尾追加
 
二进制模式(添加 'b'):
"rb"  - 二进制只读
"wb"  - 二进制只写
"ab"  - 二进制追加
"r+b" - 二进制读写(等同于 "rb+")
"w+b" - 二进制读写,覆盖
"a+b" - 二进制读写,追加
 
重要区别:
- 文本模式:自动转换换行符 (\n <-> \r\n)
- 二进制模式:不进行任何转换
*/

2.3 C风格文本文件操作详解

#include <cstdio>
#include <cstring>
 
// ========== fprintf 格式化写入 ==========
void cFormattedWrite() {
    FILE* fp = fopen("data.txt", "w");
    if (!fp) return;
    
    fprintf(fp, "姓名: %s\n", "张三");
    fprintf(fp, "年龄: %d\n", 25);
    fprintf(fp, "分数: %.2f\n", 89.56);
    fprintf(fp, "布尔值: %d\n", 1);
    fprintf(fp, "十六进制: 0x%X\n", 255);
    
    fclose(fp);
}
 
// ========== fputs 写入字符串 ==========
void cPutsWrite() {
    FILE* fp = fopen("data.txt", "w");
    if (!fp) return;
    
    fputs("这是一行文本", fp);
    fputs("\n", fp);  // 需要手动添加换行符
    fputs("第二行", fp);
    fputs("\n", fp);
    
    fclose(fp);
}
 
// ========== fputc 写入单个字符 ==========
void cPutcWrite() {
    FILE* fp = fopen("data.txt", "w");
    if (!fp) return;
    
    // 逐字符写入
    const char* str = "Hello";
    for (int i = 0; str[i] != '\0'; i++) {
        fputc(str[i], fp);
    }
    fputc('\n', fp);
    
    fclose(fp);
}
 
// ========== fscanf 格式化读取 ==========
void cFormattedRead() {
    FILE* fp = fopen("data.txt", "r");
    if (!fp) return;
    
    char name[50];
    int age;
    float score;
    
    // 从文件读取格式化数据
    int matched = fscanf(fp, "姓名: %s\n", name);
    
    if (matched == 1) {
        cout << "成功读取名字: " << name << endl;
    }
    
    fscanf(fp, "年龄: %d\n", &age);
    fscanf(fp, "分数: %f\n", &score);
    
    printf("读取数据:%s, %d, %.2f\n", name, age, score);
    
    fclose(fp);
}
 
// ========== fgets 读取一行 ==========
void cGetsRead() {
    FILE* fp = fopen("data.txt", "r");
    if (!fp) return;
    
    char line[256];
    int lineNum = 0;
    
    // fgets 会包含换行符
    while (fgets(line, sizeof(line), fp) != NULL) {
        lineNum++;
        printf("行 %d: %s", lineNum, line);  // 不需要加 endl,因为已有换行符
    }
    
    fclose(fp);
}
 
// ========== fgetc 读取单个字符 ==========
void cGetcRead() {
    FILE* fp = fopen("data.txt", "r");
    if (!fp) return;
    
    char ch;
    int charCount = 0;
    
    // 逐字符读取,直到 EOF
    while ((ch = fgetc(fp)) != EOF) {
        charCount++;
        if (ch == '\n') {
            printf("[换行符]\n");
        } else if (ch == '\t') {
            printf("[制表符]");
        } else {
            putchar(ch);
        }
    }
    
    printf("\n共读取 %d 个字符\n", charCount);
    
    fclose(fp);
}
 
// ========== fgetc 与 getc 的区别 ==========
void cGetcVsGetc() {
    FILE* fp = fopen("data.txt", "r");
    if (!fp) return;
    
    // fgetc 是函数调用
    char ch1 = fgetc(fp);
    
    // getc 是宏定义,可能更快
    char ch2 = getc(fp);
    
    // putc 是 putchar 的带文件版本
    putc(ch1, stdout);
    putc(ch2, stdout);
    
    fclose(fp);
}
 
// ========== ungetc 回退单个字符 ==========
void cUngetc() {
    FILE* fp = fopen("data.txt", "r");
    if (!fp) return;
    
    char ch1 = fgetc(fp);
    char ch2 = fgetc(fp);
    
    // 将 ch2 回退到输入流
    ungetc(ch2, fp);
    
    // 下一次 fgetc 会再次读取 ch2
    char ch3 = fgetc(fp);
    
    printf("ch2 = %c, ch3 = %c\n", ch2, ch3);
    
    fclose(fp);
}
 
// ========== fgets 与 fscanf 的区别 ==========
void cGetsVsScanf() {
    FILE* fp = fopen("data.txt", "r");
    if (!fp) return;
    
    // fgets:读取固定数量的字符或直到换行符
    char line[256];
    fgets(line, 256, fp);  // 读取最多 255 个字符
    
    // fscanf:按照格式字符串读取
    char name[50];
    int age;
    fscanf(fp, "%s %d", name, &age);  // 按格式读取
    
    fclose(fp);
}

2.4 C风格二进制文件操作详解

#include <cstdio>
#include <cstring>
 
struct Student {
    int id;
    char name[50];
    float score;
};
 
// ========== fwrite 写入二进制数据 ==========
void cWriteBinary() {
    FILE* fp = fopen("student.dat", "wb");
    
    if (!fp) {
        perror("无法打开文件");
        return;
    }
    
    // 单个结构体写入
    Student s1 = {1001, "张三", 95.5f};
    
    // fwrite(数据地址, 单个元素大小, 元素个数, 文件指针)
    // 返回成功写入的元素个数
    size_t written = fwrite(&s1, sizeof(Student), 1, fp);
    
    if (written != 1) {
        printf("写入失败,期望写入 1 个元素,实际写入 %zu\n", written);
    }
    
    // 数组写入
    Student students[3] = {
        {1002, "李四", 88.0f},
        {1003, "王五", 92.5f},
        {1004, "赵六", 85.0f}
    };
    
    written = fwrite(students, sizeof(Student), 3, fp);
    printf("成功写入 %zu 个学生记录\n", written);
    
    // 写入基本类型
    int count = 3;
    fwrite(&count, sizeof(int), 1, fp);
    
    fclose(fp);
}
 
// ========== fread 读取二进制数据 ==========
void cReadBinary() {
    FILE* fp = fopen("student.dat", "rb");
    
    if (!fp) {
        perror("无法打开文件");
        return;
    }
    
    Student s;
    
    // fread(数据地址, 单个元素大小, 元素个数, 文件指针)
    // 返回成功读取的元素个数
    while (fread(&s, sizeof(Student), 1, fp) == 1) {
        printf("学号: %d\n", s.id);
        printf("姓名: %s\n", s.name);
        printf("分数: %.2f\n", s.score);
        printf("-------------------\n");
    }
    
    fclose(fp);
}
 
// ========== 混合读写二进制文件 ==========
void cModifyBinary() {
    FILE* fp = fopen("student.dat", "r+b");
    
    if (!fp) return;
    
    Student s;
    int recordIndex = 1;  // 修改第二条记录
    
    // 定位到指定位置
    fseek(fp, recordIndex * sizeof(Student), SEEK_SET);
    
    // 读取该记录
    if (fread(&s, sizeof(Student), 1, fp) == 1) {
        printf("原始分数: %.2f\n", s.score);
        
        // 修改数据
        s.score = 99.5f;
        
        // 回到同一位置
        fseek(fp, recordIndex * sizeof(Student), SEEK_SET);
        
        // 写入修改
        fwrite(&s, sizeof(Student), 1, fp);
        printf("新分数: %.2f\n", s.score);
    }
    
    fclose(fp);
}
 
// ========== 计算二进制文件中的记录数 ==========
long cCountRecords(const char* filename, size_t recordSize) {
    FILE* fp = fopen(filename, "rb");
    if (!fp) return -1;
    
    // 定位到文件末尾
    fseek(fp, 0, SEEK_END);
    
    // 获取文件大小
    long fileSize = ftell(fp);
    
    // 计算记录数
    long recordCount = fileSize / recordSize;
    
    fclose(fp);
    
    return recordCount;
}

2.5 C风格文件指针操作详解

#include <cstdio>
 
void cFilePointer() {
    FILE* fp = fopen("data.txt", "r+b");
    
    if (!fp) return;
    
    // ========== fseek 移动文件指针 ==========
    // fseek(文件指针, 偏移量, 起始位置)
    // 返回 0 表示成功,非 0 表示失败
    
    // 移到文件开头
    if (fseek(fp, 0, SEEK_SET) != 0) {
        perror("fseek 失败");
    }
    
    // 从当前位置向后移动 10 字节
    fseek(fp, 10, SEEK_CUR);
    
    // 从文件末尾向前移动 5 字节
    fseek(fp, -5, SEEK_END);
    
    // 直接定位到第 100 字节
    fseek(fp, 100, SEEK_SET);
    
    // ========== ftell 获取当前位置 ==========
    long pos = ftell(fp);
    printf("当前文件指针位置: %ld\n", pos);
    
    if (pos == -1L) {
        perror("ftell 失败");
    }
    
    // ========== rewind 回到文件开头 ==========
    rewind(fp);  // 等价于 fseek(fp, 0, SEEK_SET)
    
    // ========== 获取文件大小 ==========
    fseek(fp, 0, SEEK_END);
    long fileSize = ftell(fp);
    printf("文件大小: %ld 字节\n", fileSize);
    rewind(fp);
    
    // ========== 在特定位置读写 ==========
    
    // 定位到第 50 字节并读取 20 字节
    fseek(fp, 50, SEEK_SET);
    char buffer[20];
    fread(buffer, 1, 20, fp);
    
    // 定位到第 100 字节并写入
    fseek(fp, 100, SEEK_SET);
    fwrite("TEST", 1, 4, fp);
    
    fclose(fp);
}
 
// ========== 一次性读取整个文件 ==========
char* cReadEntireFile(const char* filename) {
    FILE* fp = fopen(filename, "rb");
    if (!fp) return NULL;
    
    // 获取文件大小
    fseek(fp, 0, SEEK_END);
    long fileSize = ftell(fp);
    rewind(fp);
    
    // 分配内存
    char* buffer = (char*)malloc(fileSize + 1);
    if (!buffer) {
        fclose(fp);
        return NULL;
    }
    
    // 读取文件
    size_t readBytes = fread(buffer, 1, fileSize, fp);
    buffer[readBytes] = '\0';  // 字符串终结
    
    fclose(fp);
    
    return buffer;
}
 
// ========== 文件分块读取 ==========
void cChunkedRead(const char* filename) {
    FILE* fp = fopen(filename, "rb");
    if (!fp) return;
    
    const size_t CHUNK_SIZE = 1024;
    char buffer[CHUNK_SIZE];
    size_t bytesRead;
    long totalBytes = 0;
    
    // 循环读取固定大小的块
    while ((bytesRead = fread(buffer, 1, CHUNK_SIZE, fp)) > 0) {
        printf("读取 %zu 字节\n", bytesRead);
        totalBytes += bytesRead;
        
        // 处理数据...
    }
    
    printf("总共读取 %ld 字节\n", totalBytes);
    
    fclose(fp);
}

2.6 C风格错误处理详解

#include <cstdio>
#include <cerrno>
#include <cstring>
 
void cErrorHandling() {
    FILE* fp = fopen("nonexistent.txt", "r");
    
    // ========== 检查文件指针 ==========
    if (fp == NULL) {
        // perror 打印错误信息到 stderr
        perror("错误");
        
        // errno 全局变量存储错误代码
        printf("错误代码: %d\n", errno);
        
        // strerror 获取错误描述
        printf("错误描述: %s\n", strerror(errno));
        return;
    }
    
    // ========== feof 检查是否到达文件末尾 ==========
    if (feof(fp)) {
        printf("已到达文件末尾\n");
    }
    
    // ========== ferror 检查是否发生读写错误 ==========
    if (ferror(fp)) {
        printf("发生读写错误\n");
        perror("详细信息");
    }
    
    // ========== clearerr 清除错误标志 ==========
    clearerr(fp);  // 重置 EOF 和错误标志
    
    fclose(fp);
}
 
// ========== 详细的错误处理示例 ==========
int cRobustRead(const char* filename) {
    FILE* fp = fopen(filename, "r");
    
    if (fp == NULL) {
        fprintf(stderr, "无法打开文件 '%s': %s\n", 
                filename, strerror(errno));
        return -1;
    }
    
    char line[256];
    int lineNum = 0;
    
    while (fgets(line, sizeof(line), fp) != NULL) {
        lineNum++;
        printf("行 %d: %s", lineNum, line);
    }
    
    // 判断循环退出的原因
    if (feof(fp)) {
        printf("成功读取到文件末尾\n");
    } else if (ferror(fp)) {
        fprintf(stderr, "读取过程中出现错误: %s\n", 
                strerror(errno));
        fclose(fp);
        return -1;
    }
    
    fclose(fp);
    return 0;
}
 
// ========== 处理 errno ==========
void cErrnoHandling() {
    // 清除 errno
    errno = 0;
    
    FILE* fp = fopen("test.txt", "r");
    
    if (fp == NULL) {
        // errno 值对应不同的错误
        switch (errno) {
            case ENOENT:
                printf("文件不存在\n");
                break;
            case EACCES:
                printf("权限不足\n");
                break;
            case EMFILE:
                printf("打开的文件过多\n");
                break;
            default:
                printf("其他错误: %s\n", strerror(errno));
        }
    }
}

2.7 C风格 fprintf 与 printf 对比

#include <cstdio>
 
void cFprintfComparison() {
    // printf 输出到标准输出(屏幕)
    printf("这是输出到屏幕\n");
    
    FILE* fp = fopen("output.txt", "w");
    
    // fprintf 输出到指定文件
    fprintf(fp, "这是输出到文件\n");
    fprintf(fp, "数字: %d, 字符串: %s\n", 123, "hello");
    
    // fprintf 也可以输出到标准输出或错误输出
    fprintf(stdout, "标准输出\n");
    fprintf(stderr, "标准错误\n");
    
    fclose(fp);
}

三、高级文件操作

3.1 文件复制

#include <fstream>
#include <iostream>
#include <cstring>
using namespace std;
 
// ========== 文本文件复制 ==========
bool copyTextFile(const string& source, const string& dest) {
    ifstream srcFile(source);
    ofstream destFile(dest);
    
    if (!srcFile || !destFile) {
        return false;
    }
    
    string line;
    while (getline(srcFile, line)) {
        destFile << line << "\n";
    }
    
    srcFile.close();
    destFile.close();
    return true;
}
 
// ========== 二进制文件复制 ==========
bool copyBinaryFile(const string& source, const string& dest) {
    ifstream srcFile(source, ios::binary);
    ofstream destFile(dest, ios::binary);
    
    if (!srcFile || !destFile) {
        return false;
    }
    
    // 方法1:使用缓冲区复制
    destFile << srcFile.rdbuf();
    
    srcFile.close();
    destFile.close();
    return true;
}
 
// ========== 带进度的大文件复制 ==========
bool copyLargeFile(const string& source, const string& dest) {
    ifstream srcFile(source, ios::binary);
    ofstream destFile(dest, ios::binary);
    
    if (!srcFile || !destFile) {
        return false;
    }
    
    const size_t BUFFER_SIZE = 1024 * 1024;  // 1MB
    char* buffer = new char[BUFFER_SIZE];
    
    srcFile.seekg(0, ios::end);
    long totalSize = srcFile.tellg();
    srcFile.seekg(0, ios::beg);
    
    long copiedSize = 0;
    
    while (srcFile.read(buffer, BUFFER_SIZE) || srcFile.gcount() > 0) {
        destFile.write(buffer, srcFile.gcount());
        copiedSize += srcFile.gcount();
        
        cout << "进度: " << (copiedSize * 100 / totalSize) << "%" << "\r";
    }
    
    cout << "\n复制完成!" << endl;
    
    delete[] buffer;
    srcFile.close();
    destFile.close();
    return true;
}

3.2 文件合并和分割

#include <fstream>
#include <iostream>
#include <vector>
using namespace std;
 
// ========== 合并多个文件 ==========
bool mergeFiles(const vector<string>& fileList, const string& outputFile) {
    ofstream outFile(outputFile, ios::binary);
    
    if (!outFile) {
        return false;
    }
    
    for (const auto& file : fileList) {
        ifstream inFile(file, ios::binary);
        
        if (!inFile) {
            cerr << "无法打开: " << file << endl;
            continue;
        }
        
        outFile << inFile.rdbuf();
        inFile.close();
    }
    
    outFile.close();
    return true;
}
 
// ========== 分割大文件 ==========
bool splitFile(const string& sourceFile, const string& prefix, 
               long partSize) {
    ifstream srcFile(sourceFile, ios::binary);
    if (!srcFile) return false;
    
    char* buffer = new char[partSize];
    int partNum = 1;
    
    while (srcFile.read(buffer, partSize) || srcFile.gcount() > 0) {
        // 生成分片文件名
        char partFilename[256];
        sprintf(partFilename, "%s.part%d", prefix.c_str(), partNum);
        
        ofstream partFile(partFilename, ios::binary);
        partFile.write(buffer, srcFile.gcount());
        partFile.close();
        
        cout << "创建分片: " << partFilename 
             << " (" << srcFile.gcount() << " 字节)" << endl;
        
        partNum++;
    }
    
    delete[] buffer;
    srcFile.close();
    return true;
}

3.3 文件搜索和替换

#include <fstream>
#include <iostream>
#include <string>
using namespace std;
 
// ========== 搜索文件中的文本 ==========
int searchInFile(const string& filename, const string& keyword) {
    ifstream inFile(filename);
    if (!inFile) return -1;
    
    string line;
    int count = 0;
    int lineNum = 0;
    
    while (getline(inFile, line)) {
        lineNum++;
        
        // 查找关键词
        size_t pos = line.find(keyword);
        if (pos != string::npos) {
            count++;
            cout << "行 " << lineNum << ": " << line << endl;
        }
    }
    
    inFile.close();
    return count;
}
 
// ========== 替换文件中的文本 ==========
bool replaceInFile(const string& filename, 
                   const string& oldText, 
                   const string& newText) {
    ifstream inFile(filename);
    if (!inFile) return false;
    
    // 读取所有内容
    string content((istreambuf_iterator<char>(inFile)),
                   istreambuf_iterator<char>());
    inFile.close();
    
    // 替换所有出现的文本
    size_t pos = 0;
    while ((pos = content.find(oldText, pos)) != string::npos) {
        content.replace(pos, oldText.length(), newText);
        pos += newText.length();
    }
    
    // 写回文件
    ofstream outFile(filename);
    if (!outFile) return false;
    
    outFile << content;
    outFile.close();
    
    return true;
}

3.4 文件比较

#include <fstream>
#include <iostream>
using namespace std;
 
// ========== 比较两个文件是否相同 ==========
bool compareFiles(const string& file1, const string& file2) {
    ifstream f1(file1, ios::binary);
    ifstream f2(file2, ios::binary);
    
    if (!f1 || !f2) return false;
    
    // 检查文件大小
    f1.seekg(0, ios::end);
    f2.seekg(0, ios::end);
    
    if (f1.tellg() != f2.tellg()) {
        return false;
    }
    
    // 重置指针
    f1.seekg(0, ios::beg);
    f2.seekg(0, ios::beg);
    
    // 逐字符比较
    const size_t BUFFER_SIZE = 4096;
    char buffer1[BUFFER_SIZE];
    char buffer2[BUFFER_SIZE];
    
    while (f1.read(buffer1, BUFFER_SIZE) || f1.gcount() > 0) {
        f2.read(buffer2, f1.gcount());
        
        if (memcmp(buffer1, buffer2, f1.gcount()) != 0) {
            return false;
        }
    }
    
    return true;
}

四、综合示例

4.1 配置文件读取器

#include <fstream>
#include <iostream>
#include <map>
#include <string>
#include <sstream>
using namespace std;
 
class ConfigFile {
private:
    map<string, string> configs;
    string filename;
    
    string trim(const string& str) {
        size_t first = str.find_first_not_of(" \t\r\n");
        if (first == string::npos) return "";
        size_t last = str.find_last_not_of(" \t\r\n");
        return str.substr(first, (last - first + 1));
    }
    
public:
    ConfigFile(const string& fname) : filename(fname) {
        load();
    }
    
    void load() {
        ifstream file(filename);
        
        if (!file.is_open()) {
            cerr << "无法打开配置文件" << endl;
            return;
        }
        
        string line;
        while (getline(file, line)) {
            // 跳过注释和空行
            if (line.empty() || line[0] == '#') {
                continue;
            }
            
            // 查找 = 号
            size_t eqPos = line.find('=');
            if (eqPos != string::npos) {
                string key = trim(line.substr(0, eqPos));
                string value = trim(line.substr(eqPos + 1));
                configs[key] = value;
            }
        }
        
        file.close();
    }
    
    string get(const string& key, const string& defaultValue = "") {
        auto it = configs.find(key);
        return (it != configs.end()) ? it->second : defaultValue;
    }
    
    int getInt(const string& key, int defaultValue = 0) {
        string value = get(key);
        return value.empty() ? defaultValue : stoi(value);
    }
    
    float getFloat(const string& key, float defaultValue = 0.0f) {
        string value = get(key);
        return value.empty() ? defaultValue : stof(value);
    }
    
    void set(const string& key, const string& value) {
        configs[key] = value;
    }
    
    void save() {
        ofstream file(filename);
        
        if (!file.is_open()) {
            cerr << "无法保存配置文件" << endl;
            return;
        }
        
        file << "# Configuration file\n";
        file << "# 自动生成,请勿手动修改\n\n";
        
        for (const auto& pair : configs) {
            file << pair.first << "=" << pair.second << "\n";
        }
        
        file.close();
    }
};
 
// 使用示例
int main() {
    ConfigFile config("app.conf");
    
    cout << "应用名称: " << config.get("app.name", "默认应用") << endl;
    cout << "端口号: " << config.getInt("server.port", 8080) << endl;
    cout << "超时时间: " << config.getFloat("server.timeout", 30.0f) << "s" << endl;
    
    return 0;
}

4.2 CSV文件处理器

#include <fstream>
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
using namespace std;
 
class CSVReader {
private:
    vector<vector<string>> data;
    
    vector<string> parseLine(const string& line) {
        vector<string> fields;
        stringstream ss(line);
        string field;
        
        while (getline(ss, field, ',')) {
            // 去除前后空格
            size_t start = field.find_first_not_of(" \t");
            size_t end = field.find_last_not_of(" \t");
            
            if (start != string::npos) {
                fields.push_back(field.substr(start, end - start + 1));
            } else {
                fields.push_back("");
            }
        }
        
        return fields;
    }
    
public:
    bool read(const string& filename) {
        ifstream file(filename);
        
        if (!file.is_open()) {
            cerr << "无法打开CSV文件" << endl;
            return false;
        }
        
        data.clear();
        string line;
        
        while (getline(file, line)) {
            if (!line.empty()) {
                data.push_back(parseLine(line));
            }
        }
        
        file.close();
        return true;
    }
    
    void display() {
        for (size_t i = 0; i < data.size(); ++i) {
            for (size_t j = 0; j < data[i].size(); ++j) {
                cout << data[i][j];
                if (j < data[i].size() - 1) cout << " | ";
            }
            cout << "\n";
        }
    }
    
    vector<string> getRow(size_t index) {
        return (index < data.size()) ? data[index] : vector<string>();
    }
    
    size_t rowCount() const {
        return data.size();
    }
};
 
// 使用示例
int main() {
    CSVReader csv;
    
    if (csv.read("data.csv")) {
        cout << "读取了 " << csv.rowCount() << " 行数据" << endl;
        csv.display();
    }
    
    return 0;
}

五、常见问题和解决方案

5.1 文件读写常见问题

#include <fstream>
#include <iostream>
using namespace std;
 
// 问题1:文件打开失败
void problem1_FileFail() {
    ifstream file("nonexistent.txt");
    
    // 错误做法
    // string line;
    // getline(file, line);  // 直接使用,可能导致未定义行为
    
    // 正确做法
    if (!file.is_open()) {
        cerr << "文件打开失败!" << endl;
        return;
    }
    
    string line;
    getline(file, line);
    file.close();
}
 
// 问题2:混淆 ios::app 和 ios::ate
void problem2_AppVsAte() {
    // ios::app - 每次写入都在末尾,不能改变指针
    ofstream appFile("data.txt", ios::app);
    appFile << "追加内容1\n";
    // 下次写入自动在末尾,即使手动修改指针也无效
    
    // ios::ate - 打开后指针在末尾,但可以修改
    fstream ateFile("data.txt", ios::ate | ios::in | ios::out);
    ateFile.seekg(0, ios::beg);  // 可以定位
    char ch;
    ateFile.get(ch);  // 从开头读取
}
 
// 问题3:文本和二进制模式混淆
void problem3_TextVsBinary() {
    // 文本模式:自动转换换行符
    // Windows: \n <-> \r\n
    // Unix: \n 保持不变
    
    ofstream textFile("text.txt");  // 默认文本模式
    textFile << "第一行\n第二行\n";
    textFile.close();
    
    // 二进制模式:不进行任何转换
    ofstream binFile("binary.bin", ios::binary);
    binFile << "第一行\n第二行\n";  // 直接写入 \n
    binFile.close();
}
 
// 问题4:读取后没有清除状态标志
void problem4_StateFlags() {
    ifstream file("data.txt");
    
    string line;
    while (getline(file, line)) {
        cout << line << endl;
    }
    
    // 读到 EOF 后,failbit 和 eofbit 都被设置
    file.clear();  // 必须清除状态,否则后续操作失败
    
    file.seekg(0, ios::beg);  // 现在可以重新读取
    
    while (getline(file, line)) {
        cout << line << endl;
    }
    
    file.close();
}
 
// 问题5:getline 包含换行符的误解
void problem5_GetlineNewline() {
    ifstream file("data.txt");
    string line;
    
    getline(file, line);  // 不包含 \n,会自动去掉
    cout << "行长度: " << line.length() << endl;
    cout << "最后一个字符: " << (int)line.back() << endl;  // 不是 \n
}
 
// 问题6:二进制读写时忘记类型转换
void problem6_BinaryTypeCast() {
    struct Data {
        int value;
        float score;
    };
    
    ofstream file("data.bin", ios::binary);
    Data d = {100, 3.14f};
    
    // 错误做法(无法编译或产生错误)
    // file.write(&d, sizeof(d));
    
    // 正确做法
    file.write(reinterpret_cast<char*>(&d), sizeof(d));
    file.close();
}
 
// 问题7:大文件处理导致内存溢出
void problem7_LargeFile() {
    // 错误做法:一次性读入整个文件
    // ifstream file("huge.dat");
    // string content((istreambuf_iterator<char>(file)),
    //                istreambuf_iterator<char>());
    // 可能导致内存不足
    
    // 正确做法:分块读取
    ifstream file("huge.dat", ios::binary);
    const size_t CHUNK = 1024 * 1024;  // 1MB
    char buffer[CHUNK];
    
    while (file.read(buffer, CHUNK) || file.gcount() > 0) {
        // 处理数据块
        size_t bytesRead = file.gcount();
    }
    
    file.close();
}
 
// 问题8:文件指针未正确初始化
void problem8_PointerInit() {
    fstream file("data.txt", ios::in | ios::out);
    
    // 问题:直接进行操作,指针位置不确定
    // file << "数据";
    
    // 正确做法:显式初始化指针位置
    file.seekg(0, ios::beg);  // 从开头读取
    file.seekp(0, ios::end);  // 在末尾写入
}

5.2 性能优化技巧

#include <fstream>
#include <iostream>
using namespace std;
 
// 优化1:禁用同步提高性能
void optimization1_FastIO() {
    // 这些设置会加快I/O速度
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    ifstream file("large.txt");
    string line;
    
    while (getline(file, line)) {
        // 处理数据
    }
    
    file.close();
}
 
// 优化2:使用二进制模式避免转换
void optimization2_BinaryMode() {
    // 文本模式需要处理换行符转换,较慢
    // ifstream textFile("data.txt");
    
    // 二进制模式跳过转换,较快
    ifstream binFile("data.txt", ios::binary);
}
 
// 优化3:调整缓冲区大小
void optimization3_BufferSize() {
    ifstream file("large.txt");
    
    // 增大缓冲区可提高速度
    char buffer[65536];  // 64KB
    file.rdbuf()->pubsetbuf(buffer, sizeof(buffer));
    
    string line;
    while (getline(file, line)) {
        // 处理数据
    }
    
    file.close();
}
 
// 优化4:预分配容量避免重新分配
void optimization4_ReserveCapacity() {
    vector<string> lines;
    lines.reserve(10000);  // 预分配内存
    
    ifstream file("data.txt");
    string line;
    
    while (getline(file, line)) {
        lines.push_back(line);  // 避免频繁重新分配
    }
    
    file.close();
}
 
// 优化5:使用移动语义
void optimization5_MoveSemantics() {
    ifstream file("data.txt");
    vector<string> lines;
    string line;
    
    while (getline(file, line)) {
        lines.push_back(move(line));  // 避免复制
    }
    
    file.close();
}

5.3 安全编程实践

#include <fstream>
#include <iostream>
#include <memory>
using namespace std;
 
// 安全实践1:RAII管理文件资源
class SafeFileReader {
private:
    ifstream file;
    
public:
    SafeFileReader(const string& filename) {
        file.open(filename);
        if (!file) {
            throw runtime_error("无法打开文件: " + filename);
        }
    }
    
    ~SafeFileReader() {
        if (file.is_open()) {
            file.close();
        }
    }
    
    string readLine() {
        string line;
        if (!getline(file, line)) {
            throw runtime_error("读取失败");
        }
        return line;
    }
};
 
// 使用示例
void practice1_RAII() {
    try {
        SafeFileReader reader("data.txt");
        string line = reader.readLine();
        cout << line << endl;
        // 自动关闭文件
    } catch (const exception& e) {
        cerr << e.what() << endl;
    }
}
 
// 安全实践2:使用智能指针管理内存
void practice2_SmartPointer() {
    ifstream file("data.txt");
    
    // 自动管理动态分配的内存
    unique_ptr<char[]> buffer(new char[4096]);
    
    file.read(buffer.get(), 4096);
    
    // 自动释放,无需手动 delete
}
 
// 安全实践3:验证输入数据
void practice3_ValidateInput() {
    ifstream file("data.txt");
    
    int value;
    if (file >> value) {
        // 验证读取成功
        if (!file.fail()) {
            cout << "读取的值: " << value << endl;
        }
    } else {
        cerr << "读取失败或格式错误" << endl;
    }
    
    file.close();
}
 
// 安全实践4:避免缓冲区溢出
void practice4_SafeBuffer() {
    ifstream file("data.txt");
    
    const size_t BUFFER_SIZE = 256;
    char buffer[BUFFER_SIZE];
    
    // 错误做法:直接读取,可能溢出
    // file >> buffer;
    
    // 正确做法:限制读取大小
    file.read(buffer, BUFFER_SIZE - 1);
    buffer[file.gcount()] = '\0';
    
    file.close();
}

六、C++ vs C 文件操作对比表

特性C++ (fstream)C (FILE*)
类型安全✅ 强类型检查❌ 弱类型转换
异常处理✅ 支持异常机制❌ 返回错误码
自动资源管理✅ RAII自动关闭❌ 需手动关闭
操作符重载✅ 支持 << >>❌ 不支持
内存管理✅ 自动管理❌ 需手动分配
性能稍慢(多重检查)稍快
代码风格面向对象面向过程
跨平台兼容✅ 更好✅ 标准库
学习曲线较陡(需理解流)较缓(直观简单)
调试难度中等较简单
适合项目现代C++项目底层系统编程