2009-01-31 19 views
31

De acuerdo, mkstemp es la forma preferida de crear un archivo temporal en POSIX.¿Cómo crear un std :: ofstream en un archivo temporal?

Pero abre el archivo y devuelve un int, que es un descriptor de archivo. De eso solo puedo crear un ARCHIVO *, pero no un std::ofstream, que preferiría en C++. (Al parecer, en AIX y algunos otros sistemas, se puede crear un std::ofstream de un descriptor de archivo, pero mi compilador se queja cuando intento eso.)

Sé que podría obtener un nombre de archivo temporal con tmpnam y luego abrir mi propio ofstream con ella, pero eso es al parecer insegura debido a las condiciones de carrera, y da como resultado un compilador de advertencia (g ++ v3.4 en Linux.):

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

Así que, ¿hay alguna manera portátil para crear un std::ofstream a una temperatura ¿archivo?

Respuesta

10

creo que esto debería funcionar:

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

EDIT: Bueno, esto podría no ser portátil. Si no puede utilizar adjuntar y no se puede crear un ofstream directamente de un descriptor de archivo, entonces usted tiene que hacer esto:

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

Como mkstemp ya se crea el archivo para usted, condición de carrera no debe ser un problema aquí.

+0

Eso no se compila con mi g ++ v3.4.4 en Linux. Aparentemente solo algunas plataformas tienen esa función. – Frank

+0

¡Gracias! Para su segundo método (usando mkstemp y luego ofstream): ¿Sigue siendo eficiente en términos de E/S? Eso accederá al sistema de archivos dos veces, ¿verdad? Nuestro sistema de archivos es super lento y me preocupa que le imponga una carga innecesaria. – Frank

+0

strdup no es portátil, tampoco ... – Sol

16

que he hecho esta función:

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

Probablemente debería asegurarse de llamar máscara de usuario con una máscara de creación de archivos adecuado (yo preferiría 0600) - la página del manual de mkstemp dice que la máscara de modo de creación de archivos no está estandarizado Utiliza el hecho de que mkstemp modifica su argumento para el nombre de archivo que usa. Entonces, lo abrimos y cerramos el archivo que se abrió (para que no se haya abierto dos veces), quedando con un flujo de corriente que está conectado a ese archivo.

+0

Me pregunto si es seguro usar std :: string como plantilla y usar (char *) dst.path.c_str(). Parece estar bien para la mayoría de las implementaciones sensatas de std :: string. – ididak

+0

ididak, no es seguro. la cadena de caracteres apuntada no es escribible :) –

+0

En C++ 11 una std :: cadena puede tener su primera entrada desreferenciada.Por lo tanto, puede pasar & somestring [0]; a una función esperando un char *. Ver: http://en.cppreference.com/w/cpp/string/basic_string –

2

Tal vez esto funcionará:

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

Yo no lo he probado, pero se puede comprobar.

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

Este código funciona para mí con GCC/libstdC++ 6 4.8.4 y Clang 3.9 también. Gracias a las otras respuestas que fueron útiles para mí.

Cuestiones relacionadas