Cara mendapatkan pesan kesalahan saat buka ifstream gagal

99
ifstream f;
f.open(fileName);

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

Bagaimana cara mendapatkan pesan kesalahan sebagai string?

Alex F
sumber
3
kemungkinan duplikat C ++ ifstream Error Checking
Matthieu Rouget
3
@Alex Farber: Tentu. cerr << "Error code: " << strerror(errno); // Get some info as to whysepertinya relevan dengan pertanyaan itu.
Matthieu Rouget
@MatthieuRouget: Periksa kemungkinan duplikat yang saya posting - tampaknya ini adalah perilaku non-standar yang hanya diterapkan oleh gcc.
arne
1
@ MatthieuRouget: strerror(errno)bekerja. Posting ini sebagai jawaban, saya akan menerimanya.
Alex F

Jawaban:

72

Setiap panggilan sistem yang gagal memperbarui errnonilai.

Dengan demikian, Anda dapat memperoleh lebih banyak informasi tentang apa yang terjadi ketika ifstreampembukaan gagal dengan menggunakan sesuatu seperti:

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

Namun, sejak itu setiap panggilan sistem memperbarui nilai global errno, Anda mungkin mengalami masalah dalam aplikasi multithread, jika panggilan sistem lain memicu kesalahan antara eksekusi f.opendan penggunaan errno.

Pada sistem dengan standar POSIX:

errno adalah thread-local; mengaturnya di satu utas tidak memengaruhi nilainya di utas lainnya.


Sunting (terima kasih kepada Arne Mertz dan orang lain di komentar):

e.what() tampaknya pada awalnya lebih C ++ - cara yang secara idiomatis benar untuk mengimplementasikan ini, namun string yang dikembalikan oleh fungsi ini bergantung pada implementasi dan (setidaknya dalam libstdc ++ G ++) string ini tidak memiliki informasi yang berguna tentang alasan di balik kesalahan ...

Matthieu Rouget
sumber
1
e.what()sepertinya tidak memberikan banyak informasi, lihat update jawaban saya.
Arne Mertz
17
errnomenggunakan penyimpanan lokal-utas pada sistem operasi modern. Namun, tidak ada jaminan bahwa fstreamfungsi tersebut tidak akan terhenti errnosetelah terjadi errno. Fungsi yang mendasari mungkin tidak diatur errnosama sekali (panggilan sistem langsung di Linux, atau Win32). Ini tidak berfungsi pada banyak implementasi dunia nyata.
strcat
1
Di MSVC, e.what()selalu mencetak pesan yang sama " 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 Itu bohong. Abaikan atau nonaktifkan peringatan.
SS Anne
29

Anda dapat mencoba membiarkan aliran mengeluarkan pengecualian jika gagal:

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(), bagaimanapun, sepertinya tidak terlalu membantu:

  • Saya mencobanya di Win7, Embarcadero RAD Studio 2010 di mana ia memberikan "ios_base :: failbit set" sedangkan strerror(errno)memberikan "Tidak ada file atau direktori seperti itu."
  • Di Ubuntu 13.04, gcc 4.7.3 dalam pengecualian mengatakan "basic_ios :: clear" (terima kasih kepada arne )

Jika e.what()tidak berhasil untuk Anda (saya tidak tahu apa yang akan dikatakannya tentang kesalahan tersebut, karena itu tidak terstandarisasi), coba gunakan std::make_error_condition(hanya 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";
}
Arne Mertz
sumber
Terima kasih. Saya tidak menguji ini karena strerror(errno)diposting di komentar berfungsi dan sangat mudah digunakan. Saya pikir itu e.whatakan berhasil, karena errnoberhasil.
Alex F
Kemudian lihat annotaions tentang multithreading dalam jawaban Matthieus - tebakan saya adalah itulah yang e.what()akan strerrorkembali, dengan cara yang aman untuk thread. Keduanya mungkin bergantung pada platform.
Arne Mertz
1
@AlexFarber: Saya pikir jawaban Arne lebih baik dari saya. Solusi saya bukanlah C ++ - cara untuk menyelesaikan masalah Anda. Namun, saya tidak menemukan informasi resmi tentang bagaimana perpustakaan C ++ memetakan kesalahan panggilan sistem exception.what(). Semoga menjadi kesempatan yang baik untuk mempelajari kode sumber libstdc ++ :-)
Matthieu Rouget
Saya mencoba ini: Mencoba membuka file yang tidak ada dan pesan pengecualian terbaca basic_ios::clear, tidak ada yang lain. Ini tidak terlalu membantu. Itu sebabnya saya tidak memposting;)
arne
@ Sarne yang platform, kompilator, os?
Arne Mertz
22

Mengikuti jawaban @Arne Mertz, mulai C ++ 11 std::ios_base::failuremewarisi dari system_error(lihat http://www.cplusplus.com/reference/ios/ios_base/failure/ ), yang berisi kode kesalahan dan pesan yang strerror(errno)akan dikembalikan.

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

Ini mencetak No such file or directory.jika fileNametidak ada.

rthur
sumber
9
Bagi saya di MSVC 2015 yang baru saja mencetak iostream stream error.
rustyx
2
Bagi saya GCC 6.3 juga mencetak iostream error. Pada kompiler apa Anda menguji ini? Apakah ada kompilator yang benar-benar memberikan alasan kegagalan yang dapat dibaca pengguna?
Ruslan
2
Dentang 6 pada libc ++ di MacOS: unspecified iostream_category error.
akim
Xcode 10.2.1 (Clang) / libc ++ (C ++ 17) di MacOS 10.14.x: juga "Kesalahan iostream_category tidak ditentukan". strerror (errno) Tampak menjadi satu-satunya cara untuk mendapatkan hak ini. Saya kira saya bisa menangkapnya terlebih dahulu dengan menanyakan std :: filesystem apakah path.exists (), dan memeriksa std :: error_code yang dikembalikannya.
SMGreenfield
7

Anda juga bisa melempar std::system_errorseperti yang ditunjukkan pada kode tes di bawah ini. Metode ini tampaknya menghasilkan keluaran yang lebih mudah dibaca daripada 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;
}

Contoh keluaran (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)
ɲeuroburɳ
sumber