Как создать std::ofstream для временного файла?

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

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

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

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

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

4 ответа

Решение

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

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

РЕДАКТИРОВАТЬ: Ну, это не может быть портативным. Если вы не можете использовать attach и не можете создать ofstream непосредственно из файлового дескриптора, то вам нужно сделать это:

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

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

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

#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)- на странице man для mkstemp сказано, что маска создания режима файла не стандартизирована. Он использует тот факт, что mkstemp изменяет свой аргумент на имя файла, которое он использует. Итак, мы открываем его и закрываем файл, который он открыл (чтобы не открывать его дважды), оставляя его с офстримом, который связан с этим файлом.

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

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

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

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. Спасибо и другим ответам, которые были полезны для меня.

Другие вопросы по тегам