C++文件流式编程的优势详解
作者:宋康
C++文件流式编程优势
文件操作是很多程序都会用到的功能,在c语言中,常常使用FILE*指针和一系列标准库函数(如fopen、fclose、fread、fwrite、fprintf、fscanf)进行文件操作,看看一段c语言的文件操作代码
#include <stdio.h>
FILE* fp = fopen("data.txt", "r");
if (fp == NULL) { perror("打开失败"); return -1; }
int num;
while (fscanf(fp, "%d", &num) != EOF) {  // 需手动检查返回值
    printf("读取到: %d\n", num);
}
fclose(fp);  // 必须手动关闭,否则泄漏资源c语言作为一个面向过程的语言,上面的操作就充分体现了这个特点,通过函数打开——读取/写入——关闭的一系列过程,完成对文件的操作。
而且格式化写入的过程中,要靠人工保证匹配正确性, 一旦格式不匹配时就会导致运行时错误
下面来看看C++是怎么做的
#include <iostream>
#include <fstream>  // C++文件流头文件
int main() {
    std::ofstream outFile("example.txt");  // 创建输出文件流(写入模式)
    if (!outFile) {  // 检查文件是否成功打开
        std::cerr << "文件打开失败!" << std::endl;
        return 1;
    }
    outFile << "Hello, C++!" << std::endl;  // 使用 << 操作符写入数据
    outFile.close();  // 关闭文件(析构时也会自动关闭)
    return 0;
}是不是很眼熟?
写文件跟打印输出的写法很类似,把要写的内容输出到文件对象就可以了,非常直观,也不用考虑格式匹配的问题。
这就是c++的面对对象的实现方式带来的优势
从函数调用到对象交互
C++文件操作的革命性变化在于将文件操作封装为流对象,通过<<(插入)和>>(提取)运算符实现类型安全的读写。
文件操作最常用的三个类是:
ifstream:文件输入对象(读操作)ofstream:文件输出对象(写操作)fstream:文件输入输出对象(读写操作)
对文件操作时,就根据具体的操作创建不同的类对象,创建对象时指定文件就相当于打开了文件,然后对文件对象进行流操作就可以了,看看具体的例子:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() 
{
    // 写入文件
    ofstream outFile("test_cpp.txt");  //创建即打开文件
    if (!outFile.is_open()) {  //判断是否正常打开
        cerr << "无法打开文件进行写入" << endl;
        return 1;
    }
    outFile << "Hello, C++ File!" << endl;
    outFile << "这是第二行内容" << endl;
    outFile.close(); // 可以省略,析构函数会自动关闭
    // 读取文件
    ifstream inFile("test_cpp.txt");
    if (!inFile) { // 等价于!inFile.is_open()
        cerr << "无法打开文件进行读取" << endl;
        return 1;
    }
    string line;
    cout << "文件内容:" << endl;
    while (getline(inFile, line)) { // 逐行读取
        cout << line << endl;
    }
    // 检查是否正常读取结束
    if (inFile.eof()) {
        cout << "读取完成" << endl;
    } else if (inFile.fail()) {
        cerr << "读取过程中发生错误" << endl;
    }
    return 0;
}了使用流操作符>>,<<外,也可以使用read,write方法对文件进行读写,特别是对二进制文件的读写
#include <iostream>
#include <fstream>
int main() 
{
    int num = 42;
    double pi = 3.14159;
    // 写入二进制文件
    std::ofstream binOut("data.bin", std::ios::binary);
    
    binOut.write(reinterpret_cast<const char*>(&num), sizeof(num)); //写入num
    binOut.write(reinterpret_cast<const char*>(&pi), sizeof(pi)); //写入pi
    binOut.close();
    // 读取二进制文件
    std::ifstream binIn("data.bin", std::ios::binary);
    int readNum;
    double readPi;
    //读取一个int数据到readnum
    binIn.read(reinterpret_cast<char*>(&readNum), sizeof(readNum)); 
   
    //读取一个double数据到readPi
    binIn.read(reinterpret_cast<char*>(&readPi), sizeof(readPi));
    binIn.close();
    std::cout << "读取的数字: " << readNum << ", Pi: " << readPi << std::endl;
    return 0;
}RAII机制保障资源安全释放
RAII机制(资源获取即初始化)是C++流安全的基石:流对象在析构时自动调用close(),无论函数正常返回还是异常退出,都能确保资源释放。
对比C语言中需要在每个return前调用fclose的繁琐,C++实现了"一次声明,全程无忧"的资源管理。
如下面代码:
#include <fstream>
using namespace std;
int main() 
{
    ifstream in("data.txt");  // 构造即打开文件,RAII自动管理
    int num;
    // 流对象在布尔上下文自动转换为!fail(),简洁判断读取状态
    while (in >> num) {  // 类型安全:若num类型不匹配,编译报错
        cout << "读取到: " << num << endl;
    }
    // 无需手动close,in析构时自动释放资源
    return 0;
}当然,也可以使用close方法手动关闭文件流
错误处理:流状态标志
C++流对象内置状态标志,可通过good()/fail()/eof()精准判断错误类型:
ifstream in("data.txt");
if (!in) {
    if (in.eof()) cerr << "已到文件尾";
    else if (in.fail()) cerr << "格式错误";
    else if (in.bad()) cerr << "致命错误";
}对比C语言需要组合feof/ferror的繁琐判断,C++流状态管理更清晰高效。
二进制文件随机存取
c++文件流对象也支持对文件随机存取,而且定义了输入和输出两个文件指针,分别用seekg和seekp方法来获取。
另外还有提供了指示文件位置的几个常量
- ios_base::beg 文件开始处,相当于SEEK_SET
 - ios_bae::cur 当前文件指针位置,相当于SEEK_CUR
 - ios_base::end 文件结尾处,相当于SEEK_END
 
具体例子
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct Student {
    int id;
    char name[20];
    float score;
};
int main() 
{
    // 二进制写入
    ofstream outFile("students.dat", ios::binary);
    if (!outFile) {
        cerr << "无法打开文件进行写入" << endl;
        return 1;
    }
    Student s1 = {1, "张三", 90.5};
    Student s2 = {2, "李四", 85.0};
    outFile.write(reinterpret_cast<const char*>(&s1), sizeof(s1));
    outFile.write(reinterpret_cast<const char*>(&s2), sizeof(s2));
    outFile.close();
    // 二进制读取和随机访问
    ifstream inFile("students.dat", ios::binary);
    if (!inFile) {
        cerr << "无法打开文件进行读取" << endl;
        return 1;
    }
    // 移动到第二个学生的位置
    inFile.seekg(sizeof(Student), ios::beg);
    Student s;
    inFile.read(reinterpret_cast<char*>(&s), sizeof(s));
    cout << "学号: " << s.id << endl;
    cout << "姓名: " << s.name << endl;
    cout << "成绩: " << s.score << endl;
    // 获取文件大小
    inFile.seekg(0, ios::end);   //移动到文件结尾
    streampos fileSize = inFile.tellg();  //获取文件指针当前位置
    cout << "文件大小: " << fileSize << " 字节" << endl;
    cout << "学生数量: " << fileSize / sizeof(Student) << endl;
    return 0;
}顺便提一下,随机访问一般只对二进制文件使用,因为文本文件可能由于文件编码和换行符的影响导致文件指针偏移量计算出现误差,从而出错
最后做个c和c++文件操作的简单对比
对比项  | C语言(stdio.h)  | C++(<fstream>流)  | 
|---|---|---|
文件操作方式  | FILE*+ 函数(fopen、fprintf)  | 流类(ofstream、ifstream)  | 
语法  | 函数式(fprintf)  | 流式(<<和 >>)  | 
错误处理  | 返回值(如NULL)  | fail()、eof()、bad()  | 
资源管理  | 手动fclose  | RAII(自动关闭)  | 
二进制支持  | fread/fwrite  | write/read(需std::ios::binary)  | 
易用性  | 较低(函数多)  | 更高(流操作直观)  | 
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
