如何创建一个指向临时文件的std::ofstream?

37

在POSIX中,mkstemp是创建临时文件的首选方式。

但它打开文件并返回一个int,即文件描述符。我只能从中创建一个FILE*,而不能在C++中使用我更喜欢的std::ofstream。(显然,在AIX和其他一些系统上,您可以从文件描述符创建一个std::ofstream,但当我尝试时,我的编译器会发出警告。)

我知道可以使用tmpnam获取临时文件名,然后使用它自己打开ofstream,但由于竞态条件,这似乎不安全,并导致编译器警告(Linux上的g++ v3.4.):

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

那么,有没有可移植的方法来创建一个指向临时文件的std::ofstream

4个回答

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

你应该确保使用适当的文件创建掩码(我建议使用0600)来调用umask - mkstemp的manpage说,文件模式创建掩码没有标准化。它利用了mkstemp修改其参数为所使用的文件名的事实。因此,我们打开并关闭它打开的文件(以免它被打开两次),留下一个连接到该文件的ofstream。


我在想,是否可以安全地将std :: string用作模板,并使用(char *)dst.path.c_str()。对于大多数合理的std :: string实现来说,这似乎是可以的。 - ididak
ididak,这不安全。指向的C字符串不可写 :) - Johannes Schaub - litb
在C++11中,std::string可以对其第一个条目进行取消引用。因此,您可以将&somestring [0];传递给期望char *的函数。请参阅:http://en.cppreference.com/w/cpp/string/basic_string - X-Istence
@X-Istence:是的,但在GCC [pre-5.1](https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html)中要小心:https://dev59.com/hGct5IYBdhLWcg3wSrl3 - Lightness Races in Orbit

11

我认为这应该可以工作:

    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已经为您创建了文件,因此在这里不应该存在竞争条件问题。


这在我的 Linux 上的 g++ v3.4.4 上编译不通过。显然,只有一些平台有那个函数。 - Frank
谢谢!关于你的第二种方法(使用mkstemp然后ofstream):从I/O的角度来看,它仍然有效吗?那会访问文件系统两次,对吧?我们的文件系统非常慢,我担心这会给它带来不必要的负担。 - Frank
3
mkstemp 可能会失败并返回 -1;你的代码应该捕获这种情况。 - R Samuel Klatchko
另外,第二个示例泄漏了mkstemp返回的文件描述符。 - adl
4
使用 strdup 后,您还需要调用 free - dreamlax
mkstemp 不具备可移植性? std::tmpfile 具备可移携性,但似乎并不是很有用。 - lmat - Reinstate Monica

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上都能正常工作。同时也感谢其他回答对我有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接