2009-01-31 2 views
31

Хорошо, mkstemp - предпочтительный способ создания временного файла в POSIX.Как создать std :: ofstream для временного файла?

Но он открывает файл и возвращает int, который является файловым дескриптором. Из этого я могу создать только FILE *, но не std::ofstream, который я бы предпочел в C++. (По-видимому, на AIX и некоторые другие системы, вы можете создать std::ofstream из файлового дескриптора, но мой компилятор жалуется, когда я пытаюсь это.)

Я знаю, что я мог бы получить имя временного файла с tmpnam, а затем открыть мои собственные ofstream с ним, но это, по-видимому небезопасным из-за условия гонки, и результаты предупреждения компилятора (г ++ v3.4 на Linux.):

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

Итак, есть ли какой-либо портативный способ создания std::ofstream на темп файл?

ответ

10

Я думаю, что это должно работать:

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

EDIT: Ну, это могло бы не быть портативным. Если вы не можете использовать прикрепиться и не может создать ofstream непосредственно из файлового дескриптора, то вы должны сделать это:

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

Как mkstemp уже создает для вас файл, расы условие не должно быть проблема здесь.

+0

Это не компилируется с моим g ++ v3.4.4 на Linux. По-видимому, только некоторые платформы имеют эту функцию. – Frank

+0

Спасибо! Для вашего второго метода (с использованием mkstemp и thenstream): Это все еще эффективно с точки зрения ввода-вывода? Это будет доступ к файловой системе дважды, не так ли? Наша файловая система очень медленная, и я беспокоюсь, что она наложит на нее ненужную нагрузку. – Frank

+0

strdup не переносится, либо ... – Sol

16

Я сделал эту функцию:

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

Вы, вероятно, следует убедиться, что называть Umask с маской создания собственно файла (я бы предпочел 0600) - страница руководства для mkstemp говорит, что маска создания файлов не стандартизирована. Он использует тот факт, что mkstemp изменяет свой аргумент на имя файла, которое он использует. Таким образом, мы открываем его и закрываем файл, который он открывал (чтобы его не открывали дважды), оставаясь с потоком, подключенным к этому файлу.

+0

Интересно, можно ли просто использовать std :: string в качестве шаблона и использовать (char *) dst.path.c_str(). Кажется, это прекрасно для наиболее разумных реализаций std :: string. – ididak

+0

ididak, это не безопасно. c-строка, на которую указана ссылка, не доступна для записи :) –

+0

В C++ 11 строка std :: может иметь свою первую запись разыменовываться.Таким образом, вы можете передать & somestring [0]; к функции, ожидающей char *. См .: http://en.cppreference.com/w/cpp/string/basic_string –

2

Может быть, это будет работать:

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

Я не пробовал, но вы можете проверить.

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

Этот код работает для меня с GCC/libstdC++ 6 4.8.4 и Clang 3.9. Спасибо другим ответам, которые были полезны для меня.

Смежные вопросы