2009-01-31 5 views
31

Ok, mkstemp est le moyen préféré pour créer un fichier temporaire dans POSIX.Comment créer un fichier std :: ofstream dans un fichier temporaire?

Mais il ouvre le fichier et renvoie un int, qui est un descripteur de fichier. De cela, je ne peux que créer un fichier *, mais pas un std::ofstream, que je préférerais en C++. (Apparemment, sur AIX et d'autres systèmes, vous pouvez créer un std::ofstream d'un descripteur de fichier, mais mon compilateur se plaint quand j'essayer.)

Je sais que je pourrais obtenir un nom de fichier temporaire avec tmpnam puis ouvrir mon propre ofstream avec elle, mais c'est apparemment dangereux en raison des conditions de course, et les résultats dans un avertissement compilateur (g ++ v3.4 sur Linux.):

warning: the use of `tmpnam' is dangerous, better use `mkstemp' 

Alors, est-il un moyen portable pour créer un std::ofstream à une température fichier?

Répondre

10

Je pense que cela devrait fonctionner:

char *tmpname = strdup("/tmp/tmpfileXXXXXX"); 
    ofstream f; 
    int fd = mkstemp(tmpname); 
    f.attach(fd); 

EDIT: Eh bien, cela pourrait ne pas être portable. Si vous ne pouvez pas utiliser attacher et ne peut pas créer un ofstream directement à partir d'un descripteur de fichier, vous devez faire ceci:

char *tmpname = strdup("/tmp/tmpfileXXXXXX"); 
mkstemp(tmpname); 
ofstream f(tmpname); 

Comme mkstemp crée déjà le fichier pour vous, l'état de la course ne devrait pas être un problème ici.

+0

Cela ne compile pas avec mon g ++ v3.4.4 sous Linux. Apparemment, seules certaines plateformes ont cette fonction. – Frank

+0

Merci! Pour votre seconde méthode (en utilisant mkstemp puis ofstream): Est-ce encore efficace en termes d'E/S? Cela va accéder au système de fichiers deux fois, non? Notre système de fichiers est super lent et je crains que cela ne lui impose un fardeau inutile. – Frank

+0

strdup n'est pas portable, non plus ... – Sol

16

Je l'ai fait cette fonction:

#include <stdlib.h> 
#include <fstream> 
#include <iostream> 
#include <vector> 

std::string open_temp(std::string path, std::ofstream& f) { 
    path += "/XXXXXX"; 
    std::vector<char> dst_path(path.begin(), path.end()); 
    dst_path.push_back('\0'); 

    int fd = mkstemp(&dst_path[0]); 
    if(fd != -1) { 
     path.assign(dst_path.begin(), dst_path.end() - 1); 
     f.open(path.c_str(), 
       std::ios_base::trunc | std::ios_base::out); 
     close(fd); 
    } 
    return path; 
} 

int main() { 
    std::ofstream logfile; 
    open_temp("/tmp", logfile); 
    if(logfile.is_open()) { 
     logfile << "hello, dude" << std::endl; 
    } 
} 

Vous devriez probablement vous assurer d'appeler umask avec un masque de création de fichier approprié (je préférerais 0600) - la page de manuel pour mkstemp dit que le mode fichier masque de création n'est pas standardisé. Il utilise le fait que mkstemp modifie son argument au nom de fichier qu'il utilise. Donc, nous l'ouvrons et fermons le fichier qu'il a ouvert (donc, pour ne pas l'avoir ouvert deux fois), étant laissé avec un ofstream qui est connecté à ce fichier.

+0

Je me demande s'il est prudent d'utiliser simplement std :: string comme modèle et d'utiliser (char *) dst.path.c_str(). Semble être bien pour les implémentations les plus sensibles de std :: string. – ididak

+0

ididak, ce n'est pas sûr. la chaîne de caractères pointée vers n'est pas accessible en écriture :) –

+0

En C++ 11, std :: string peut avoir sa première entrée déréférencée.Ainsi vous pouvez passer & somestring [0]; à une fonction qui attend un caractère *. Voir: http://en.cppreference.com/w/cpp/string/basic_string –

2

Peut-être que cela fonctionnera:

char tmpname[256]; 
ofstream f; 
sprintf (tmpname, "/tmp/tmpfileXXXXXX"); 
int fd = mkstemp(tmpname); 
ofstream f(tmpname); 

Je ne l'ai pas essayé, mais vous pouvez vérifier.

0
char tempFileName[20]; // name only valid till next invocation of tempFileOpen 
ofstream tempFile; 
void tempFileOpen() 
{ 
    strcpy(tempFileName, "/tmp/XXXXXX"); 
    mkstemp(tempFileName); 
    tempFile.open(tempFileName); 
} 

Ce code fonctionne pour moi avec GCC/libstdC++ 6 4.8.4 et Clang 3.9 également. Merci aux autres réponses qui m'ont été utiles.