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

时间:2013-06-27 07:51:38

标签: c++ error-handling stream std

ifstream f;
f.open(fileName);

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

如何以字符串形式获取错误消息?

5 个答案:

答案 0 :(得分:61)

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

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

cerr << "Error: " << strerror(errno);

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

在具有POSIX标准的系统上:

  

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


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

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

答案 1 :(得分:24)

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

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';
}
然而,

e.what()似乎没有用处:

  • 我在Win7,Embarcadero RAD Studio 2010上尝试了它,它提供了“ios_base :: failbit set”,而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";
}

答案 2 :(得分:16)

关于@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;
}

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

答案 3 :(得分:3)

您还可以抛出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;
}

示例输出(带叮当声的Ubuntu):

$ ./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)

答案 4 :(得分:0)

上面的 std::system_error 示例有点不正确。 std::system_category() 将映射来自系统本机错误代码工具的错误代码。对于 *nix,这是 errno。对于 Win32,它是 GetLastError()。即,在 Windows 上,上面的示例将打印

failed to open C:\path\to\forbidden: The data is invalid

因为 EACCES 是 13,这是 Win32 错误代码 ERROR_INVALID_DATA

要修复它,请使用系统的本机错误代码工具,例如在 Win32 上

throw new std::system_error(GetLastError(), std::system_category(), "failed to open"+ filename);

或者使用 errno 和 std::generic_category(),例如

throw new std::system_error(errno, std::generic_category(), "failed to open"+ filename);