So erhalten Sie eine Fehlermeldung, wenn das Öffnen des ifstreams fehlschlägt

99
ifstream f;
f.open(fileName);

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

Wie erhalte ich eine Fehlermeldung als Zeichenfolge?

Alex F.
quelle
3
Mögliches Duplikat von C ++ ifstream Error Checking
Matthieu Rouget
3
@ Alexander Farber: Sicher. cerr << "Error code: " << strerror(errno); // Get some info as to whyscheint für die Frage relevant zu sein.
Matthieu Rouget
@MatthieuRouget: Überprüfen Sie das mögliche Duplikat, das ich gepostet habe - es scheint, dass dies kein Standardverhalten ist, das nur von gcc implementiert wird.
Arne
1
@MatthieuRouget: strerror(errno)funktioniert. Poste dies als Antwort, ich werde es akzeptieren.
Alex F

Antworten:

71

Jeder fehlgeschlagene Systemaufruf aktualisiert den errnoWert.

Auf diese Weise können Sie weitere Informationen darüber erhalten, was passiert, wenn ein ifstreamÖffnen fehlschlägt, indem Sie Folgendes verwenden:

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

Da jedoch jeder Systemaufruf den globalen errnoWert aktualisiert , können Probleme in einer Multithread-Anwendung auftreten, wenn ein anderer Systemaufruf einen Fehler zwischen der Ausführung von f.openund der Verwendung von auslöst errno.

Auf einem System mit POSIX-Standard:

errno ist threadlokal; Das Festlegen in einem Thread wirkt sich nicht auf den Wert in einem anderen Thread aus.


Bearbeiten (danke an Arne Mertz und andere Leute in den Kommentaren):

e.what() schien zunächst eine C ++ - idiomatisch korrektere Art der Implementierung zu sein, jedoch ist die von dieser Funktion zurückgegebene Zeichenfolge implementierungsabhängig und (zumindest in G ++ 's libstdc ++) enthält diese Zeichenfolge keine nützlichen Informationen über den Grund für den Fehler ...

Matthieu Rouget
quelle
1
e.what()scheint nicht viele Informationen zu geben, siehe Aktualisierungen meiner Antwort.
Arne Mertz
17
errnoVerwendet Thread-lokalen Speicher auf modernen Betriebssystemen. Es gibt jedoch keine Garantie dafür, dass die fstreamFunktionen errnonach einem Fehler nicht mehr funktionieren. Die zugrunde liegenden Funktionen sind möglicherweise überhaupt nicht festgelegt errno(direkte Systemaufrufe unter Linux oder Win32). Dies funktioniert bei vielen Implementierungen in der realen Welt nicht.
Strcat
1
Druckt in MSVC e.what()immer die gleiche Nachricht " 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
@ergiol Das sind Lügen. Ignorieren Sie sie oder deaktivieren Sie die Warnung.
SS Anne
29

Sie können versuchen, den Stream bei einem Fehler eine Ausnahme auslösen zu lassen:

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()scheint jedoch nicht sehr hilfreich zu sein:

  • Ich habe es unter Win7, Embarcadero RAD Studio 2010 versucht, wo es "ios_base :: failbit set" strerror(errno)gibt, während es "Keine solche Datei oder kein solches Verzeichnis" gibt.
  • Unter Ubuntu 13.04, gcc 4.7.3 lautet die Ausnahme "basic_ios :: clear" (danke an arne )

Wenn e.what()dies bei Ihnen nicht funktioniert (ich weiß nicht, was es Ihnen über den Fehler aussagt, da dieser nicht standardisiert ist), versuchen Sie es mit std::make_error_condition(nur 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
quelle
Vielen Dank. Ich habe dies nicht getestet, da strerror(errno)es in den Kommentaren veröffentlicht funktioniert und sehr einfach zu bedienen ist. Ich denke das e.whatwird funktionieren, da errnofunktioniert.
Alex F
Dann sehen Sie sich die Anmerkungen zum Multithreading in Matthieus 'Antwort an - ich vermute, das e.what()wird auf strerrorthreadsichere Weise zurückkehren. Beide werden wahrscheinlich plattformabhängig sein.
Arne Mertz
1
@AlexFarber: Ich denke, dass Arnes Antwort besser ist als meine. Meine Lösung ist nicht die C ++ - Methode zur Lösung Ihres Problems. Ich habe jedoch keine offiziellen Informationen darüber gefunden, wie die C ++ - Bibliothek Systemaufruffehler zuordnet exception.what(). Kann eine gute Gelegenheit sein, in den libstdc ++ Quellcode einzutauchen :-)
Matthieu Rouget
Ich habe Folgendes ausprobiert: Es wurde versucht, eine nicht vorhandene Datei zu öffnen und die Ausnahmemeldung zu lesen basic_ios::clear, sonst nichts. Das ist nicht wirklich hilfreich. Deshalb habe ich nicht gepostet;)
Arne
@arne welche Plattform, Compiler, OS?
Arne Mertz
22

Im Anschluss an die Antwort von @Arne Mertz std::ios_base::failureerbt ab C ++ 11 von system_error(siehe http://www.cplusplus.com/reference/ios/ios_base/failure/ ), die sowohl den Fehlercode als auch die Nachricht enthält, strerror(errno)die zurückkehren würde.

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

Dies wird gedruckt, No such file or directory.wenn fileNamees nicht existiert.

rthur
quelle
8
Für mich in MSVC 2015 druckt das nur iostream stream error.
Rustyx
2
Für mich druckt GCC 6.3 auch iostream error. Auf welchem ​​Compiler haben Sie das getestet? Gibt ein Compiler tatsächlich einen vom Benutzer lesbaren Grund für einen Fehler an?
Ruslan
2
Clang 6 unter libc ++ unter macOS : unspecified iostream_category error.
Akim
Xcode 10.2.1 (Clang) / libc ++ (C ++ 17) unter MacOS 10.14.x: auch "Nicht angegebener Fehler iostream_category". strerror (errno) scheint der einzige Weg zu sein, dies richtig zu machen. Ich nehme an, ich könnte es zuerst abfangen, indem ich std :: filesystem frage, ob path.exists () vorhanden ist, und den zurückgegebenen std :: error_code untersuche.
SMGreenfield
7

Sie können auch einen werfen, std::system_errorwie im folgenden Testcode gezeigt. Diese Methode scheint eine besser lesbare Ausgabe zu erzeugen als 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;
}

Beispielausgabe (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)
"Neurobur"
quelle