ifstream打开失败时如何获取错误消息

Ale*_*x F 92 c++ error-handling std stream

ifstream f;
f.open(fileName);

if ( f.fail() )
{
    // I need error message here, like "File not found" etc. -
    // the reason of the failure
}
Run Code Online (Sandbox Code Playgroud)

如何将错误消息作为字符串?

Mat*_*get 68

每次失败的系统调用都会更新errno值.

因此,您可以ifstream通过使用以下内容获得有关打开失败时所发生情况的更多信息:

cerr << "Error: " << strerror(errno);
Run Code Online (Sandbox Code Playgroud)

但是,由于每个系统调用都会更新全局errno值,因此如果另一个系统调用在执行f.open和使用之间触发错误,则可能在多线程应用程序中出现问题errno.

在具有POSIX标准的系统上:

errno是线程本地的; 在一个线程中设置它不会影响其在任何其他线程中的值.


编辑(感谢Arne Mertz和评论中的其他人):

e.what() 起初似乎是一个更加C++ - 惯用的方法来实现它,但是这个函数返回的字符串是依赖于实现的(至少在G ++的libstdc ++中),这个字符串没有关于错误背后原因的有用信息......

  • `errno`在现代操作系统上使用线程本地存储.然而,在发生错误之后,无法保证`fstream`函数不会破坏`errno`.底层函数可能根本不设置`errno`(Linux或Win32上的直接系统调用).这对许多现实世界的实现都不起作用. (16认同)
  • `e.what()` 似乎没有提供太多信息,请参阅我的答案的更新。 (2认同)
  • 在 MSVC 中,`e.what()` 总是打印相同的消息“`iostream stream error`” (2认同)
  • @sergiol那些都是谎言。忽略它们或禁用警告。 (2认同)

Arn*_*rtz 28

您可以尝试让流在失败时抛出异常:

std::ifstream f;
//prepare f to throw if failbit gets set
std::ios_base::iostate exceptionMask = f.exceptions() | std::ios::failbit;
f.exceptions(exceptionMask);

try {
  f.open(fileName);
}
catch (std::ios_base::failure& e) {
  std::cerr << e.what() << '\n';
}
Run Code Online (Sandbox Code Playgroud)

e.what()但是,似乎没有什么帮助:

  • 我试了一下Win7上,Embarcadero公司RAD Studio 2010中它赋予"的ios_base :: failbit集",而strerror(errno)让"没有这样的文件或目录."
  • 在Ubuntu 13.04上,gcc 4.7.3异常说"basic_ios :: clear"(感谢arne)

如果e.what()不适合你(我不知道它会告诉你的错误,因为这是不规范),请尝试使用std::make_error_condition(C++ 11只):

catch (std::ios_base::failure& e) {
  if ( e.code() == std::make_error_condition(std::io_errc::stream) )
    std::cerr << "Stream error!\n"; 
  else
    std::cerr << "Unknown failure opening file.\n";
}
Run Code Online (Sandbox Code Playgroud)

  • @AlexFarber:我认为 Arne 的回答比我的好。我的解决方案不是解决您的问题的_C++-way_。但是,我没有找到有关 C++ 库如何将系统调用错误映射到 `exception.what()` 的官方信息。可能是深入了解 libstdc++ 源代码的好机会 :-) (2认同)

rth*_*hur 19

关于@Arne Mertz的答案,从C++ 11开始std::ios_base::failure继承system_error(参见http://www.cplusplus.com/reference/ios/ios_base/failure/),其中包含错误代码和strerror(errno)将返回的消息.

std::ifstream f;

// Set exceptions to be thrown on failure
f.exceptions(std::ifstream::failbit | std::ifstream::badbit);

try {
    f.open(fileName);
} catch (std::system_error& e) {
    std::cerr << e.code().message() << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

No such file or directory.如果fileName不存在则打印.

  • 对于MSVC 2015中我只打印"iostream stream error". (7认同)
  • 对我来说,GCC 6.3也会输出iostream错误。您在哪个编译器上对此进行了测试?是否有任何编译器实际上提供了用户可读的失败原因? (2认同)
  • macOS上libc ++上的Clang 6:“未指定iostream_category错误”。 (2认同)

ɲeu*_*urɳ 10

您也可以抛出 a std::system_error,如下面的测试代码所示。这种方法似乎比f.exception(...).

#include <exception> // <-- requires this
#include <fstream>
#include <iostream>

void process(const std::string& fileName) {
    std::ifstream f;
    f.open(fileName);

    // after open, check f and throw std::system_error with the errno
    if (!f)
        throw std::system_error(errno, std::system_category(), "failed to open "+fileName);

    std::clog << "opened " << fileName << std::endl;
}

int main(int argc, char* argv[]) {
    try {
        process(argv[1]);
    } catch (const std::system_error& e) {
        std::clog << e.what() << " (" << e.code() << ")" << std::endl;
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

示例输出(Ubuntu w/clang):

$ ./test /root/.profile
failed to open /root/.profile: Permission denied (system:13)
$ ./test missing.txt
failed to open missing.txt: No such file or directory (system:2)
$ ./test ./test
opened ./test
$ ./test $(printf '%0999x')
failed to open 000...000: File name too long (system:36)
Run Code Online (Sandbox Code Playgroud)