İfstream open başarısız olduğunda hata mesajı nasıl alınır


101
ifstream f;
f.open(fileName);

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

Hata mesajı string olarak nasıl alınır?




3
@Alex Farber: Elbette. cerr << "Error code: " << strerror(errno); // Get some info as to whysoruyla alakalı görünüyor.
Matthieu Rouget

@MatthieuRouget: Gönderdiğim olası kopyayı kontrol edin - bu sadece gcc tarafından uygulanan standart dışı bir davranış gibi görünüyor.
arne

1
@MatthieuRouget: strerror(errno)çalışır. Bunu cevap olarak gönder, kabul edeceğim.
Alex F

Yanıtlar:


75

Başarısız olan her sistem çağrısı errnodeğeri günceller .

Böylece, aşağıdakiler gibi bir ifstreamşey kullanarak bir açık başarısız olduğunda ne olacağı hakkında daha fazla bilgi edinebilirsiniz :

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

Ancak, her sistem çağrısı genel errnodeğeri güncellediğinden, çok iş parçacıklı bir uygulamada sorun yaşayabilirsiniz, eğer başka bir sistem çağrısı işleminin yürütülmesi f.openile errno.

POSIX standardına sahip sistemde:

errno iş parçacığı yereldir; bunu bir iş parçacığında ayarlamak, diğer iş parçacığındaki değerini etkilemez.


Düzenleme (Arne Mertz ve yorumlardaki diğer insanlara teşekkürler):

e.what() ilk bakışta daha C ++ gibi görünüyordu - bunu uygulamanın deyimsel olarak doğru yolu, ancak bu işlev tarafından döndürülen dize uygulamaya bağlıdır ve (en azından G ++ 'ın libstdc ++' da) bu dizge, hatanın arkasındaki neden hakkında hiçbir yararlı bilgiye sahip değildir ...


1
e.what()fazla bilgi vermiyor, cevabımın güncellemelerine bakın.
Arne Mertz

17
errnomodern işletim sistemlerinde iş parçacığı yerel depolamayı kullanır. Ancak, bir hata oluştuktan sonra fstreamişlevlerin bozulmayacağının garantisi yoktur errno. Temel işlevler hiç ayarlanmayabilir errno(Linux veya Win32'de doğrudan sistem çağrıları). Bu pek çok gerçek dünya uygulamasında işe yaramaz.
strcat

1
MSVC olarak, e.what()hep aynı mesajı "yazdırır 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 Bunlar yalan. Bunları göz ardı edin veya uyarıyı devre dışı bırakın.
SS Anne

30

Akışın hata durumunda bir istisna atmasına izin vermeyi deneyebilirsiniz:

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()ancak çok yardımcı görünmüyor:

  • Win7, Embarcadero RAD Studio 2010'da denedim, burada "ios_base :: failbit set" verirken strerror(errno)"Böyle bir dosya veya dizin yok."
  • Ubuntu 13.04 tarihinde, gcc 4.7.3 istisna "basic_ios :: berrak" (sayesinde diyor Arne )

Eğer e.what()işi sizin için değil, kullanmayı deneyin (bunu standardize değil çünkü, hata hakkında söyleyecektir bilmiyorum) std::make_error_condition(C ++ 11 için):

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

Teşekkürler. Bunu test etmedim çünkü strerror(errno)yorumlarda yayınlandı ve kullanımı çok basit. Çalıştığından e.whatberi errnoişe yarayacağını düşünüyorum .
Alex F

Sonra, Matthieus cevabındaki çoklu okuma ile ilgili ek açıklamaları görün - benim tahminim bu e.what(), iş parçacığı güvenli strerrorbir şekilde geri dönecek. Her ikisi de muhtemelen platforma bağlı olacaktır.
Arne Mertz

1
@AlexFarber: Arne'nin cevabının benimkinden daha iyi olduğunu düşünüyorum. Benim çözümüm, sorununuzu çözmenin C ++ yolu değil . Bununla birlikte, C ++ kitaplığının sistem çağrısı hatalarını nasıl eşleştirdiği hakkında resmi bilgi bulamadım exception.what(). Libstdc ++ kaynak koduna dalmak için iyi bir fırsat olabilir :-)
Matthieu Rouget

Bunu denedim: Varolmayan bir dosyayı açmaya çalıştım ve istisna mesajı okundu basic_ios::clear, başka bir şey yok. Bu gerçekten yardımcı olmuyor. Bu yüzden göndermedim;)
arne

@arne hangi platform, derleyici, işletim sistemi?
Arne Mertz

23

@Arne Mertz'in cevabını takiben, C ++ 11'den std::ios_base::failureitibaren system_error(bkz. Http://www.cplusplus.com/reference/ios/ios_base/failure/ ), hem hata kodunu hem de strerror(errno)geri dönecek mesajı içeren .

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

Bu No such file or directory., fileNameyoksa yazdırılır .


9
Benim için sadece baskı yapan MSVC 2015'te iostream stream error.
rustyx

2
Benim için GCC 6.3 de yazdırıyor iostream error. Bunu hangi derleyicide test ettiniz? Herhangi bir derleyici gerçekten hata için kullanıcı tarafından okunabilir bir neden sağlıyor mu?
Ruslan

2
MacOS üzerinde Libc ++ üzerinde Clang 6: unspecified iostream_category error.
akim

MacOS 10.14.x üzerinde Xcode 10.2.1 (Clang) / libc ++ (C ++ 17): ayrıca "Belirtilmemiş iostream_category hatası". strerror (errno) Bunu doğru yapmanın tek yolu SEEMS. Sanırım önce std :: dosya sistemine path.exists () olup olmadığını sorarak ve döndürdüğü std :: error_code'u inceleyerek onu yakalayabilirim.
SMGreenfield

Örnek programda, ifade , türetilen f.open(fileName)türden bir istisna atar . İstisna, catch bloğu tarafından yakalanır. Catch bloğu içinde, türden bir nesne döndüren çağırır . Sınıf tarafından tanımlı hata kodları olan platforma bağımlı --ie, ve her iki dönüş platforma göre değerleri. std::ios_base::failurestd::system_errore.code()std::ios_base::failure::code()std::error_codestd::error_codee.code().message()e.code().value()
Jim Fischer

9

std::system_errorAşağıdaki test kodunda gösterildiği gibi bir de atabilirsiniz . Bu yöntem, daha okunabilir çıktı üretiyor gibi görünüyor 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;
}

Örnek çıktı (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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.