ifstreamのオープンが失敗したときにエラーメッセージを取得する方法


99
ifstream f;
f.open(fileName);

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

エラーメッセージを文字列として取得する方法は?




3
@アレックスファーバー:確かに。cerr << "Error code: " << strerror(errno); // Get some info as to why質問に関連しているようです。
Matthieu Rouget 2013年

@MatthieuRouget:私が投稿した重複の可能性を確認してください-これはgccによってのみ実装された非標準の動作のようです。
arne 2013年

1
@MatthieuRouget:strerror(errno)機能します。これを回答として投稿してください。
Alex F

回答:


71

失敗したすべてのシステムコールはerrno値を更新します。

したがって、次のifstreamようなものを使用して、openが失敗したときに何が起こるかについての詳細情報を得ることができます。

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

ただし、すべてのシステムコールがグローバルerrno値を更新するため、の実行f.openとの使用の間に別のシステムコールがエラーをトリガーすると、マルチスレッドアプリケーションで問題が発生する可能性がありますerrno

POSIX標準のシステム:

errnoはスレッドローカルです。1つのスレッドで設定しても、他のスレッドの値には影響しません。


編集(コメントでArne Mertzおよび他の人々に感謝):

e.what() 最初はこれを実装するためのよりC ++慣用的に正しい方法のように見えましたが、この関数によって返される文字列は実装に依存し、(少なくともG ++のlibstdc ++では)この文字列にはエラーの背後にある理由に関する有用な情報がありません...


1
e.what()多くの情報を提供していないようです、私の回答の更新を参照してください。
Arne Mertz 2013年

17
errno最新のオペレーティングシステムでスレッドローカルストレージを使用します。ただし、errnoが発生した後にfstream関数が正常に動作しないという保証はありませんerrno。基礎となる関数がまったく設定さerrnoれていない可能性があります(LinuxまたはWin32の直接システムコール)。これは、多くの実際の実装では機能しません。
strcat 2014年

1
MSVCでは、e.what()常に同じメッセージ「iostream stream error」を出力します
rustyx

warning C4996: 'strerror': This function or variable may be unsafe. Consider using strerror_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. 1> C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\string.h(168) : see declaration of 'strerror'
sergiol

1
@sergiolそれらは嘘です。それらを無視するか、警告を無効にします。
SS Anne、

29

失敗時にストリームに例外をスローさせることができます。

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";
}

ありがとう。私はこれをテストしませんでしstrerror(errno)た。コメントで投稿されたものが機能し、使用するのは非常に簡単だからです。e.whatうまくいくので、errnoうまくいくと思います。
Alex F

そして、Matthieusの答えにマルチスレッドについてannotaionsを参照してください-私の推測では、それがあるe.what()何になりますstrerrorスレッドセーフな方法で戻ります。どちらもおそらくプラットフォームに依存します。
Arne Mertz 2013年

1
@AlexFarber:Arneの答えは私の答えよりも優れていると思います。私の解決策は、問題を解決するC ++の方法ではありません。ただし、C ++ライブラリがシステムコールエラーをにマップする方法に関する公式情報は見つかりませんでしたexception.what()。libstdc ++ソースコードに飛び込むよい機会かもしれません:-)
Matthieu Rouget 2013年

私はこれを試しました:存在しないファイルを開こうとし、例外メッセージが読み込まれましたbasic_ios::clear。これはあまり役に立ちません。それが私が投稿しなかった理由です;)
arne

@arne wichプラットフォーム、コンパイラ、OS?
Arne Mertz 2013年

22

@Arne Mertzの回答に続き、C ++ 11 std::ios_base::failuresystem_errorhttp://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します。


8
MSVC 2015の私にとっては、単に印刷されるだけiostream stream errorです。
rustyx

2
私にとって、GCC 6.3も印刷しiostream errorます。どのコンパイラでこれをテストしましたか?実際に、ユーザーが読み取り可能な失敗の理由を提供するコンパイラーはありますか?
ルスラン

2
MacOSでのlibc ++のクラン6: unspecified iostream_category error
akim

MacOS 10.14.x上のXcode 10.2.1(Clang)/ libc ++(C ++ 17):「未指定のiostream_categoryエラー」も。strerror(errno)SEEMSは、これを正しく行う唯一の方法です。path.exists()かどうかをstd :: filesystemに問い合わせ、それが返すstd :: error_codeを調べることで、最初にそれをキャッチできると思います。
SMGreenfield

7

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 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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.