2010-04-09 9 views
2

Quelle est la bonne façon de vérifier une erreur générale lors de l'envoi de données à un fstream?Quelle est la bonne façon de faire une vérification d'erreur 'catch all' sur une opération de sortie fstream?

MISE À JOUR: Ma principale préoccupation concerne certaines choses que j'ai entendues au sujet d'un délai entre la sortie et toute donnée physiquement écrite sur le disque dur. Ma supposition était que la commande "save_file_obj < < save_str" enverrait seulement des données à une sorte de tampon et que la coche suivante "if (save_file_obj.bad())" ne serait d'aucune utilité pour déterminer s'il y avait un OS ou un matériel problème. Je voulais juste savoir quel était le moyen "catch all" définitif d'envoyer une chaîne à un fichier et vérifier pour s'assurer qu'il était écrit sur le disque, avant d'effectuer les actions suivantes telles que la fermeture du programme.

Je le code suivant ...

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

Répondre

2

Tout sauf pour le contrôle après la clôture semble raisonnable. Cela dit, je restructure les choses un peu différemment et jette une exception ou d'utiliser un bool, mais qui est tout simplement une question de préférence:

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

Je tiens également à souligner que vous n'avez pas besoin d'utiliser save_str.c_str(), depuis Les iostreams C++ (y compris fstream, ofstream, etc.) sont tous capables de sortir des objets std :: string. En outre, si vous construisez l'objet de flux de fichier dans la portée de la fonction, il sera automatiquement fermé lorsqu'il sort de la portée.

+0

Qui a dit 'save_str' est un' std :: string'? : p Peut-être que c'est un modèle 'std :: basic_string <>' sur des 'char_traits' personnalisés, comme des traits insensibles à la casse ou quelque chose comme ça? : p – wilhelmtell

+0

Dans ce cas, c'est std :: basic_string Truncheon

2

Etes-vous absolument sur que save_file_handle n'a pas encore de fichier associé (ouvert) avec? Si tel est le cas, l'appel de sa méthode open() échouera et déclenchera son indicateur d'erreur ios::failbit - et toute exception si elle est définie pour le faire.

La méthode close() ne peut pas échouer sauf si le fichier n'est pas ouvert, auquel cas la méthode déclenchera l'indicateur d'erreur ios::failbit. Quoi qu'il en soit, le destructeur doit fermer le fichier, et le faire automatiquement si le save_file_handle est une variable de pile comme dans votre code.

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

Vous pouvez séparer l'erreur de vérification de la logique de sauvegarde des fichiers, si vous utilisez 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; 
} 

Vous préférez peut-être passer l'appel à save_file_handle.exceptions(ios::failbit | ios::badbit) au constructeur (s). Ensuite, vous pouvez vous débarrasser des instructions qui réinitialisent l'indicateur d'exceptions.

+0

Je suggère de ne pas utiliser d'exceptions avec les E/S de flux, car peu de personnes s'attendront à lancer des opérations de flux, et parce que la récupération est plus difficile. –

4

Quelques points. Premièrement:

save_file_handle 

est un nom médiocre pour une instance d'un fstream C++. Les fstreams ne sont pas des handles de fichiers et tout ce que cela peut faire c'est confondre le lecteur.

Deuxièmement, comme Michael pinte, il n'y a pas besoin de convertir une chaîne C++ en une chaîne de caractères. La seule fois où vous devriez vraiment faire cela est quand vous interfacer avec APIS de style C, et lorsque vous utilisez quelques API C++ mal conçues, comme (malheureusement) fstream :: open(). Troisièmement, la manière canonique de tester si une opération de flux fonctionne est de tester l'opération elle-même.Cours d'eau ont une conversion en void * qui signifie que vous pouvez écrire des trucs comme ça:

if (save_file_handle << save_str) { 
    // operation worked 
} 
else { 
    // failed for some reason 
} 

Votre code devrait toujours testv les opérations de flux, que ce soit en entrée ou en sortie.

+0

Je pense que vous vouliez dire conversion à bool, pas à vide *. –

+0

@Michael Je veux dire ce que j'ai dit - la norme spécifie une conversion en void *. –

+2

@Neil, tout le monde, tu as raison ... J'ai toujours pensé que c'était booléen (puisque void * se convertit implicitement en bool). Je n'ai jamais réalisé que c'était réellement vide *. C'est intéressant, savez-vous pourquoi ils ont choisi cette convention? Peut-on réellement faire quelque chose avec l'objet void *? –