Come ottenere un messaggio di errore quando l'apertura di ifstream fallisce


99
ifstream f;
f.open(fileName);

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

Come ottenere un messaggio di errore come stringa?


3


3
@Alex Farber: certo. cerr << "Error code: " << strerror(errno); // Get some info as to whysembra pertinente alla domanda.
Matthieu Rouget,

@ MatthieuRouget: controlla l'eventuale duplicato che ho postato - sembra che questo sia un comportamento non standard implementato solo da gcc.
arne

1
@ MatthieuRouget: strerror(errno)funziona. Pubblica questo come risposta, lo accetterò.
Alex F

Risposte:


72

Ogni chiamata di sistema che non riesce aggiorna il errnovalore.

Pertanto, puoi avere più informazioni su cosa succede quando ifstreamun'apertura fallisce usando qualcosa come:

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

Tuttavia, poiché ogni chiamata di sistema aggiorna il errnovalore globale , potresti avere problemi in un'applicazione multithread, se un'altra chiamata di sistema attiva un errore tra l'esecuzione di f.opene l'uso di errno.

Su sistema con standard POSIX:

errno è locale di thread; impostarlo in un thread non influisce sul suo valore in nessun altro thread.


modificare (grazie ad Arne Mertz e altre persone nei commenti):

e.what() all'inizio sembrava essere un modo più C ++ - idiomaticamente corretto di implementarlo, tuttavia la stringa restituita da questa funzione dipende dall'implementazione e (almeno nella libstdc ++ di G ++) questa stringa non ha informazioni utili sul motivo dell'errore ...


1
e.what()non sembra dare molte informazioni, vedere gli aggiornamenti alla mia risposta.
Arne Mertz

17
errnoutilizza l'archiviazione locale del thread sui sistemi operativi moderni. Tuttavia, non vi è alcuna garanzia che le fstreamfunzioni non si bloccheranno errnodopo che si è verificato un errore. Le funzioni sottostanti potrebbero non essere impostate errnoaffatto (chiamate di sistema dirette su Linux o Win32). Questo non funziona su molte implementazioni del mondo reale.
strcat

1
In MSVC, e.what()stampa sempre lo stesso messaggio " 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 Quelle sono bugie. Ignorali o disabilita l'avviso.
SS Anne

29

Potresti provare a lasciare che il flusso generi un'eccezione in caso di errore:

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(), tuttavia, non sembra essere molto utile:

  • L'ho provato su Win7, Embarcadero RAD Studio 2010 dove dà "ios_base :: failbit set" mentre strerror(errno)dà "No such file or directory".
  • Su Ubuntu 13.04, gcc 4.7.3 l'eccezione dice "basic_ios :: clear" (grazie ad arne )

Se e.what()non funziona per te (non so cosa ti dirà dell'errore, poiché non è standardizzato), prova a utilizzare std::make_error_condition(solo 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";
}

Grazie. Non l'ho testato perché strerror(errno)pubblicato nei commenti funziona ed è molto semplice da usare. Penso che e.whatfunzionerà, dal momento che errnofunziona.
Alex F

Quindi guarda le annotazioni sul multithreading nella risposta di Matthieus: la mia ipotesi è che e.what()sarà ciò che strerrorritorna, in modo sicuro per i thread. Entrambi dipenderanno probabilmente dalla piattaforma.
Arne Mertz

1
@AlexFarber: Penso che la risposta di Arne sia migliore della mia. La mia soluzione non è il modo C ++ per risolvere il tuo problema. Tuttavia, non ho trovato informazioni ufficiali su come la libreria C ++ associa gli errori di chiamata di sistema a exception.what(). Potrebbe essere una buona opportunità per immergersi nel codice sorgente di libstdc ++ :-)
Matthieu Rouget,

Ho provato questo: ho provato ad aprire un file inesistente e il messaggio di eccezione letto basic_ios::clear, nient'altro. Questo non è molto utile. Ecco perché non ho postato;)
arne

@arne quale piattaforma, compilatore, sistema operativo?
Arne Mertz

22

Seguendo la risposta di @Arne Mertz, a partire da C ++ 11 std::ios_base::failureeredita da system_error(vedi http://www.cplusplus.com/reference/ios/ios_base/failure/ ), che contiene sia il codice di errore che il messaggio che strerror(errno)verrebbe restituito.

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

Viene stampato No such file or directory.se fileNamenon esiste.


8
Per me in MSVC 2015 che stampa solo iostream stream error.
rustyx

2
Per me GCC 6.3 stampa anche iostream error. Su quale compilatore l'hai testato? Qualche compilatore fornisce effettivamente un motivo di errore leggibile dall'utente?
Ruslan

2
Clang 6 su libc ++ su MacOS: unspecified iostream_category error.
akim

Xcode 10.2.1 (Clang) / libc ++ (C ++ 17) su MacOS 10.14.x: anche "Errore iostream_category non specificato". strerror (errno) SEMBRA essere l'unico modo per farlo bene. Suppongo di poterlo catturare prima chiedendo a std :: filesystem se path.exists () ed esaminando std :: error_code che restituisce.
SMGreenfield

7

Puoi anche lanciare un std::system_errorcome mostrato nel codice di prova qui sotto. Questo metodo sembra produrre un output più leggibile di 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;
}

Output di esempio (Ubuntu con 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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.