Qual è il modo corretto di fare un controllo errore di 'catturare tutti' su un'operazione di uscita fstream?
Domanda
Qual è il modo corretto per verificare la presenza di un errore generale durante l'invio dei dati a un fstream?
Aggiorna : La mia preoccupazione principale riguarda alcune cose che ho sentito parlare di un ritardo tra l'uscita e tutti i dati che vengono scritti fisicamente sul disco rigido. La mia ipotesi è che il "save_file_obj << save_str" comando sarà solo inviare i dati a un qualche tipo di buffer e che il seguente controllo "if (save_file_obj.bad ())" non sarebbe alcun uso per determinare se ci fosse un sistema operativo o hardware problema. Volevo solo sapere che cosa è stato il definitivo "catch all" modo di inviare una stringa in un file e verificare per accertarsi che sia stato scritto sul disco, prima di effettuare qualsiasi seguenti azioni come la chiusura del programma.
ho il seguente codice ...
int Saver::output()
{
save_file_handle.open(file_name.c_str());
if (save_file_handle.is_open())
{
save_file_handle << save_str.c_str();
if (save_file_handle.bad())
{
x_message("Error - failed to save file");
return 0;
}
save_file_handle.close();
if (save_file_handle.bad())
{
x_message("Error - failed to save file");
return 0;
}
return 1;
}
else
{
x_message("Error - couldn't open save file");
return 0;
}
}
Soluzione
Tutto tranne per il controllo dopo la chiusura sembra ragionevole. Detto questo, vorrei cose ristrutturare in modo leggermente diverso e un'eccezione o utilizzare un bool
, ma che è semplicemente una questione di preferenza:
bool Saver::output() { std::fstream out(_filename.c_str(),std::ios::out); if ( ! out.is_open() ){ LOG4CXX_ERROR(_logger,"Could not open \""<<filename<<"\""); return false; } out << _savestr << std::endl; if ( out.bad() ){ LOG4CXX_ERROR(_logger,"Could not save to \""<<filename<<"\""); out.close(); return false; } out.close(); return true; }
Vorrei anche sottolineare che non è necessario per l'uso save_str.c_str()
, dal momento che C ++ iostreams (compresi fstream, ofstream, ecc) sono tutti in grado di emettere oggetti std :: string. Inoltre, se si costruisce l'oggetto flusso di file nel campo di applicazione della funzione, verrà automaticamente chiuso quando si va fuori del campo di applicazione.
Altri suggerimenti
Alcuni punti. In primo luogo:
save_file_handle
è un nome povero per un'istanza di un fstream C ++. fstreams non sono handle di file e tutto questo può fare è confondere il lettore.
In secondo luogo, come Michael pinte fuori, non c'è bisogno di convertire una stringa C ++ per un C-string. L'unica volta che si dovrebbe davvero trovare te fare questo è quando ci si interfaccia con le API in stile C, e quando si utilizza alcune API mal progettati C ++, come (purtroppo) fstream :: open ().
In terzo luogo, il modo canonico di test se un'operazione di flusso lavorato è quello di testare l'operazione stessa. Flussi hanno una conversione a * vuoto che significa che è possibile scrivere cose come questa:
if ( save_file_handle << save_str ) {
// operation worked
}
else {
// failed for some reason
}
Il codice dovrebbe sempre testv streaming operazioni, sia per ingresso o uscita.
Sei assolutamente sicuro che save_file_handle
non dispone già di un file associato (aperto) con esso? Se è così allora chiamando il suo metodo open()
fallirà e alzare la sua bandiera errore ios::failbit
-. Ed eventuali eccezioni se impostata a farlo
Il metodo close()
non può non meno che il file non è aperto, nel qual caso il metodo si alza il flag di errore ios::failbit
. In ogni caso, il distruttore dovrebbe chiudere il file, e lo fanno automaticamente se il save_file_handle
è una variabile stack come nel codice.
int Saver::output()
{
save_file_handle.open(file_name.c_str());
if (save_file_handle.fail())
{
x_message("Error - file failed to previously close");
return 0;
}
save_file_handle << save_str.c_str();
if (save_file_handle.bad())
{
x_message("Error - failed to save file");
return 0;
}
return 1;
}
In alternativa, è possibile separare il controllo degli errori dalla logica di salvataggio dei file se si utilizza ios::exceptions()
.
int Saver::output()
{
ios_base::iostate old = save_file_handle.exceptions();
save_file_handle.exceptions(ios::failbit | ios::badbit);
try
{
save_file_handle.open(file_name.c_str());
save_file_handle << save_str.c_str();
}
catch (ofstream::failure e)
{
x_message("Error - couldn't save file");
save_file_handle.exceptions(old);
return 0;
}
save_file_handle.exceptions(old);
return 1;
}
Si potrebbe preferire di passare la chiamata al save_file_handle.exceptions(ios::failbit | ios::badbit)
al costruttore (s). Poi si può sbarazzarsi delle dichiarazioni che resettano la bandiera eccezioni.