【问题标题】:fstream EOF unexpectedly throwing exceptionfstream EOF 意外抛出异常
【发布时间】:2014-08-08 23:11:35
【问题描述】:

我的问题与a previous one 非常相似。我想打开并读取一个文件。如果无法打开文件,我希望抛出异常,但我不希望在 EOF 上抛出异常。 fstreams 似乎可以让您独立控制是否在 EOF、失败和其他不良事件上引发异常,但似乎 EOF 也倾向于映射到错误和/或失败异常。

这是我尝试做的一个精简示例。如果文件包含某个单词,函数 f() 应该返回 true,如果不包含,则返回 false,如果(比如说)文件不存在,则抛出异常。

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

using namespace std;

bool f(const char *file)
{
    ifstream ifs;
    string word;

    ifs.exceptions(ifstream::failbit | ifstream::badbit);
    ifs.open(file);

    while(ifs >> word) {
        if(word == "foo") return true;
    }
    return false;
}

int main(int argc, char **argv)
{
    try {
        bool r = f(argv[1]);
        cout << "f returned " << r << endl;
    } catch(...) {
        cerr << "exception" << endl;
    }
}

但它不起作用,因为使用 operator>> 的基本 fstream 读取显然是 EOF 设置坏或失败位的操作之一。如果文件存在且不包含“foo”,则该函数不会根据需要返回 false,而是抛出异常。

【问题讨论】:

    标签: c++ exception fstream eof


    【解决方案1】:

    std::ios_base::failbit 标志也会在文件到达末尾时尝试提取时设置,这是流的布尔运算符的行为所允许的。您应该在f() 中设置一个额外的 try-catch 块,如果它与文件结束条件不对应,则重新抛出异常:

    std::ifstream ifs;
    std::string word;
    
    ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
    
    try {
        ifs.open(file);
        while (ifs >> word) {
            if (word == "foo") return true;
        }
     }
    catch (std::ios_base::failure&) {
        if (!ifs.eof())
            throw;
    }
    return false;
    

    【讨论】:

    • 仅仅调用std::find 是行不通的,因为实际代码对文件进行了稍微复杂的分析,而不是仅仅寻找一个词。附言我喜欢你的用户名。 1131770981.
    • 对于您的第一个建议,而不是while(ifs &gt;&gt; word) { ... },我必须使用while(true) { try { if(!(ifs &gt;&gt; word)) break; } catch (...) { ... } ... 之类的东西?
    • @SteveSummit 你为什么要使用这样的东西?
    • 没关系,我误解了你的建议。 (当你说“额外的 try-catch 块”时,我在想循环内的 try/catch 块,我没有看到你提供的代码确实有必要的“额外的 try-catch 块”,在 f () 但在循环之外。)
    【解决方案2】:

    如果目标是仅在打开文件时出现问题时抛出异常,为什么不写:

    bool f(const char *file)
    {
        ifstream ifs;
        string word;
    
        ifs.open(file);
        if (ifs.fail())  // throw only when needed 
            throw std::exception("Cannot open file !");  // more accurate exception
    
        while (ifs >> word) {
            if (word == "foo") return true;
        }
        return false;
    }
    

    你当然可以设置:

    ifs.exceptions(ifstream::badbit);
    

    在打开之前或之后,抛出异常以防在阅读过程中发生非常糟糕的事情。

    【讨论】:

    • 谢谢。这是一个非常好的建议,对于我的情况可能是最直接的。 (我认为应该可以做得更好,但正如@Igor Tandetnik 解释的那样,我反对的 C++ 行为是非常基本的。在拼命尝试之间也有一个非常真实的权衡为了准确地实现我认为我想要的行为,使用各种自定义异常处理来解决 C++ 的各种特性,而不是保持代码的可读性。)
    • 不过还有一件事:我不同意“无法打开文件!”是一个更准确的例外。如果我让ifs.open 抛出它自己的异常,可能会有关于它无法打开它的为什么有用的额外信息,例如“权限被拒绝”或“没有这样的文件或目录”。 (不过,这一次,在这种特殊情况下,我不会介意没有那些额外的信息,无论如何用户都不会看到这些信息。)
    • 这是真的!但这取决于实现:例如,MSVC13 的标准异常 e.what() 在文件丢失时仅返回 "Exception ios_base::failbit set: iostream stream error"。然而,throw std::exception(strerror(errno)); 的替代方案可能是因为 C++11 带有更长的可移植错误消息列表。
    【解决方案3】:

    basic_ios::operator bool() 检查fail(),而不是!good()。到达 EOF 后,您的循环会尝试再读取一个单词。 operator&gt;&gt;(stream&amp;, string&amp;) 设置 failbit 如果没有提取字符。这就是为什么你总是以异常退出的原因。

    但很难避免这种情况。流不是在读取最后一个字符时达到 EOF 状态,而是在尝试读取最后一个字符时。如果这发生在单词中间,则failbit 未设置。如果它发生在开头(例如,如果输入有尾随空格),则设置failbit。你不能真正可靠地以eof() &amp;&amp; !fail() 状态结束。

    【讨论】:

    • 非常感谢您的解释(以及对我之前看到的标准的引用,有人——也许你——似乎已经发布然后删除了)。我看起来很无辜的代码(我从别人那里继承的,顺便说一句)真的试图在 EOF 之后阅读结尾吗?当。在我的书中,这是一个大罪。难怪它不起作用。
    猜你喜欢
    • 2012-12-27
    • 1970-01-01
    • 2019-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多