2009-01-31 27 views
31

Ok, mkstemp è il modo preferito per creare un file temporaneo in POSIX.Come creare un file std :: ofstream in un file temporaneo?

Ma apre il file e restituisce un int, che è un descrittore di file. Da quello posso solo creare un FILE *, ma non uno std::ofstream, che preferirei in C++. (A quanto pare, su AIX e altri sistemi, è possibile creare un std::ofstream da un descrittore di file, ma il mio compilatore si lamenta quando provo questo.)

So che avrei potuto ottenere un nome di file temporaneo con tmpnam e quindi aprire la mia ofstream con esso, ma questo è a quanto pare non sicuri a causa di condizioni di gara, e si traduce in un compilatore di avviso (g v3.4 ++ su Linux.):

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

Quindi, c'è un modo portatile per creare un std::ofstream ad una temperatura file?

risposta

10

penso che questo dovrebbe funzionare:

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

EDIT: Beh, questo potrebbe non essere portatile. Se non è possibile utilizzare collegare e non è possibile creare un ofstream direttamente da un descrittore di file, allora devi fare questo:

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

Come mkstemp crea già il file per voi, condizione di competizione non dovrebbe essere un problema qui.

+0

Questo non viene compilato con il mio g ++ v3.4.4 su Linux. Apparentemente solo alcune piattaforme hanno questa funzione. – Frank

+0

Grazie! Per il tuo secondo metodo (usando mkstemp e poi ofstream): è ancora efficiente in termini di I/O? Questo accederà al file system due volte, giusto? Il nostro file system è molto lento e temo che ciò comporterà un carico inutile. – Frank

+0

strdup non è portatile, o ... – Sol

16

ho fatto questa funzione:

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

Probabilmente si dovrebbe fare in modo di chiamare umask con una maschera di creazione file corretto (io preferirei 0600) - la pagina di manuale per mkstemp dice che la maschera di creazione modalità file non è standardizzato Usa il fatto che mkstemp modifica il suo argomento al nome del file che usa. Quindi, lo apriamo e chiudiamo il file che è stato aperto (quindi, per non farlo aprire due volte), rimanendo con un ofstream connesso a quel file.

+0

Mi chiedo se è sicuro usare semplicemente std :: string come template e usare (char *) dst.path.c_str(). Sembra essere a posto per le implementazioni più sensate di std :: string. – ididak

+0

ididak, non è sicuro. la c-string puntata a non è scrivibile :) –

+0

In C++ 11 a std :: string può avere la prima voce dereferenziata.Così puoi passare & somestring [0]; a una funzione che si aspetta un carattere *. Vedi: http://en.cppreference.com/w/cpp/string/basic_string –

2

Forse questo funzionerà:

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

non l'ho provato, ma si può controllare.

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

Questo codice funziona anche con GCC/libstdC++ 6 4.8.4 e Clang 3.9. Grazie anche alle altre risposte che mi sono state utili.

Problemi correlati