fstream EOF 意外抛出异常

fstream EOF unexpectedly throwing exception

提问人:Steve Summit 提问时间:8/9/2014 最后编辑:CommunitySteve Summit 更新时间:8/9/2014 访问量:2931

问:

我的问题与上一个问题非常相似。我想打开并读取一个文件。如果文件无法打开,我希望抛出异常,但我不希望在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++ 异常 fstream eof

评论


答:

1赞 Christophe 8/9/2014 #1

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

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);

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

评论

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

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

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;

评论

0赞 Steve Summit 8/9/2014
仅仅调用 std::find 是行不通的,因为实际代码对文件进行更详细的分析,而不仅仅是查找一个单词。P.S. 我喜欢你的用户名。1131770981.
0赞 Steve Summit 8/9/2014
对于您的第一个建议,而不是 ,我必须使用类似 ?while(ifs >> word) { ... }while(true) { try { if(!(ifs >> word)) break; } catch (...) { ... } ...
0赞 David G 8/9/2014
@SteveSummit 你为什么要使用这样的东西?
0赞 Steve Summit 8/9/2014
没关系,我误解了你的建议。(当你说“额外的 try-catch 块”时,我想到了循环内的 try/catch 块,我没有看到你提供的代码确实有必要的“额外的 try-catch 块”,在 f() 内部但在循环之外。
1赞 Igor Tandetnik 8/9/2014 #3

basic_ios::operator bool()检查 ,而不是 .在达到 EOF 后,您的循环会尝试再读一个单词。 如果未提取任何字符,则设置。这就是为什么你总是在有例外的情况下退出。fail()!good()operator>>(stream&, string&)failbit

不过,这很难避免。流不是在读取最后一个字符时达到 EOF 状态,而是在尝试读取最后一个字符时达到 EOF 状态。如果这发生在单词的中间,则未设置。如果它发生在开头(例如,如果输入有尾随空格),则设置为。你不可能真正可靠地最终进入状态。failbitfailbiteof() && !fail()

评论

0赞 Steve Summit 8/9/2014
非常感谢这个解释(以及我之前看到的《标准》的引用,有人——也许是你——似乎已经发布了然后删除了)。我看起来无辜的代码(顺便说一句,我从别人那里继承的)真的试图在 EOF 之后读完结尾吗?当。在我的书中,这是一个主要的罪过。难怪它不起作用。