You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

添加ifstream exceptions后循环抛出std::ios_base::failure异常的原因

ifstream设置异常掩码后读取循环抛出未处理异常问题解决

问题描述

我编写了如下C++代码,用于练习try/catch处理ifstream与ofstream的文件操作异常:

int main() {
    string path = "D:\\myFile.txt";
    Point pointOfIllusion(1, 2, 3);
    Point pointOfDesilusion(3, 2, 1);
    ofstream fout;
    
    fout.exceptions(ofstream::badbit | ofstream::failbit); // everything is ok with this string
    try {
        cout << "Trying to read the file\n";
        fout.open(path, ofstream::app); 
        cout << "The file has been successfully created and opened\n";
    }
    catch(const ofstream::failure& ex){
        cout << ex.what() << endl;
        cout << ex.code() << endl;
        perror ("Could not open the file");
        abort();
    }
    fout.write((char*)&pointOfIllusion, sizeof(Point));
    fout.write((char*)&pointOfDesilusion, sizeof(Point));

    fout.close();
    
    ifstream fin;
    fin.exceptions(ifstream::badbit | ifstream::failbit); // If I put this, it causes an error, described below, but without this string the following try/catch doesn't work
    try {
        cout << "Trying to read the file\n";
        fin.open(path);
        cout << "The file has been successfully opened\n";
    }
    catch (const ifstream::failure& ex) {
        cout << ex.what() << endl;
        cout << ex.code() << endl;
        perror ("Could not open the file");
        abort();
    }
    
    Point tempPoint;
    size_t length = 0;
    while (fin.read((char*)&tempPoint, sizeof(Point))) { // The debugger throws the exception when I add the string above in this place, however I do not modify it at all
        length++;
    }

    fin.clear();
    fin.seekg(0, ios::beg);
    
    Point* Points = new Point[length];
    for (size_t i = 0; i < length; i++)
        {
            fin.read((char*)&Points[i], sizeof(Point));
            Points[i].Print();
        }
        delete[] Points;
        
    fin.close();
    getchar();
    return 0;
}

添加fin.exceptions(ifstream::badbit | ifstream::failbit)语句后,执行while (fin.read((char*)&tempPoint, sizeof(Point)))循环时会抛出以下异常:

An exception was raised at address 0x0000007FFECCC3CF19 in WorkWithFiles.exe: Microsoft C++: std::ios_base::failure exception at memory address 0x0000000059CBAFEF80.
An unhandled exception occurred at address 0x0000007FFECCCCC3CF19 in WorkWithFiles.exe: Microsoft C++ exception: std::ios_base::failure at memory address 0x00000059CBAFEF80.

若不添加该语句,程序可正常运行,但第二个try/catch块无法捕获ifstream相关的异常。请问我哪里出错了?我正在练习使用try/catch处理ifstream/ofstream的文件打开异常,尝试过正确和错误的文件路径。

问题原因

你设置的异常掩码ifstream::badbit | ifstream::failbit会让ifstream在读取到文件末尾(EOF)且读取失败时抛出异常。当while (fin.read(...))循环执行完最后一个有效Point的读取后,下一次read尝试读取超出文件内容的部分,此时会触发failbit(因为无法读取足够的字节数),而你已经配置该位触发异常,但这段读取代码没有对应的catch块处理,就导致了未处理异常。

默认情况下,read操作到EOF时只会设置eofbit,不会触发failbit,但当读取的字节数不足要求的sizeof(Point)时(比如文件末尾剩下的字节不够一个Point),会同时设置eofbit和failbit——你的文件刚好是两个完整的Point,最后一次read成功后,下一次read直接触发failbit,进而抛出异常。

解决方案

方案1:调整异常掩码,仅捕获严重错误

如果你只需要捕获文件打开失败这类严重错误,不需要捕获读取到EOF的情况,可以修改异常掩码,只监听badbit(表示底层流出现不可恢复的错误):

fin.exceptions(ifstream::badbit);

这样既可以捕获文件打开时的严重异常,又不会在读取到EOF时抛出异常,循环可以正常终止。

方案2:给读取循环添加异常捕获

如果必须保留当前的异常掩码,需要给读取循环加上try/catch块,区分正常EOF和读取错误:

Point tempPoint;
size_t length = 0;
try {
    while (fin.read((char*)&tempPoint, sizeof(Point))) {
        length++;
    }
}
catch (const ifstream::failure& ex) {
    // 检查是否是EOF导致的异常,正常到达末尾则无需终止程序
    if (!fin.eof()) {
        cout << ex.what() << endl;
        perror("Read file failed");
        abort();
    }
}

方案3:提前计算文件大小,跳过循环计数

可以通过文件大小直接计算能读取的Point数量,避免循环读取到EOF:

fin.seekg(0, ios::end);
size_t fileSize = fin.tellg();
size_t length = fileSize / sizeof(Point);
fin.seekg(0, ios::beg);

Point* Points = new Point[length];
for (size_t i = 0; i < length; i++) {
    fin.read((char*)&Points[i], sizeof(Point));
    Points[i].Print();
}
delete[] Points;

内容的提问来源于stack exchange,提问作者Alma Latina

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 00:24:53