2009-01-31 14 views
31

Okay, mkstemp to preferowany sposób tworzenia pliku tymczasowego w POSIX.Jak utworzyć std :: ofstream do pliku tymczasowego?

Ale otwiera plik i zwraca int, który jest deskryptorem pliku. Z tego mogę utworzyć tylko PLIK *, ale nie std::ofstream, który wolałbym w C++. (Wydaje się, że w systemie AIX oraz niektórych innych systemów, można stworzyć std::ofstream z deskryptora pliku, ale mój kompilator narzeka, gdy próbuję tego.)

wiem, mogę dostać nazwę pliku temp z tmpnam a następnie otworzyć własną rękę ofstream z nim, ale to najwyraźniej niebezpieczna z powodu warunków wyścigowych i powoduje kompilator ostrzegawcze (g ++ v3.4 Linux.):

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

Więc jest jakiś przenośny sposób stworzyć std::ofstream do temp plik?

Odpowiedz

10

myślę, że to powinno działać:

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

EDIT: No, to może nie być przenośny. Jeśli nie można użyć dołączyć i nie można utworzyć ofstream bezpośrednio z deskryptora pliku, to trzeba to zrobić:

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

Jak mkstemp już tworzy plik dla ciebie, stan rasy nie powinny być problem tutaj.

+0

To nie kompiluje się z moim g ++ v3.4.4 na Linux. Podobno tylko niektóre platformy mają tę funkcję. – Frank

+0

Dzięki! Dla twojej drugiej metody (przy użyciu mkstemp, a następnie dla strumienia): Czy to nadal jest wydajne pod względem I/O? To spowoduje dwukrotny dostęp do systemu plików, prawda? Nasz system plików działa bardzo wolno i obawiam się, że spowoduje to niepotrzebne obciążenie. – Frank

+0

Strdup nie jest przenośny, albo ... – Sol

16

Zrobiłem tę funkcję:

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

Powinieneś raczej upewnij się, aby zadzwonić umask z maską tworzenia odpowiedniego pliku (wolałbym 0600) - manpage dla mkstemp mówi, że w trybie tworzenia maski plik nie jest standaryzowany. Wykorzystuje fakt, że mkstemp modyfikuje swój argument do nazwy pliku, którego używa. Otwieramy go i zamykamy otwierany plik (aby nie otwierać go dwukrotnie), pozostawiając strumień danych połączony z tym plikiem.

+0

Zastanawiam się, czy można bezpiecznie użyć std :: string jako szablonu i użyć (char *) dst.path.c_str(). Wydaje się być dobrym dla najbardziej sensownych implementacji std :: string. – ididak

+0

ididak, to nie jest bezpieczne. wskazany c-string nie jest zapisywalny :) –

+0

W C++ 11 std :: string może mieć swój pierwszy wpis dereferencyjny.W ten sposób możesz przekazać & somestring [0]; do funkcji oczekującej znaku *. Zobacz: http://en.cppreference.com/w/cpp/string/basic_string –

2

Może to będzie działać:

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

nie próbowałem, ale można sprawdzić.

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

Ten kod działa dla mnie z GCC/libstdC++ 6 4.8.4 i Clang 3.9. Dzięki innym odpowiedziom również mi pomogły.

Powiązane problemy