[英]Safe use of std::tmpnam
因此,我正在開發一個應用程序,它需要創建一個文件,寫入其中,使用該文件作為輸入來調用另一個程序,然后刪除該文件。
我尋找了可能的解決方案,一個解決方案看起來像這樣。
std::FILE* tmpf = std::tmpfile();
std::fputs("Hello, world", tmpf);
根據std::tmpfile
的文檔,如果手動關閉了文件,或者程序以自然方式退出,則該文件將被刪除。 這看起來是個好的解決方案,但有一個例外。 看起來很亂(使用CI / O而不是C ++流)。
另一個解決方案是使用std::tmpnam
,它將生成唯一的文件名。
std::string file_name = std::tmpname(NULL);
// (*)
std::fstream stream{file_name};
stream << "Hello World" << std::endl;
但是這個也有問題。 如果另一個程序創建的文件具有相同的名稱,而我的程序位於(*)
,則我們都將對同一文件執行操作,這當然是我要避免的事情。
C ++ STL(仍然)不支持文件系統操作,例如檢查文件是否存在。 我可以使用stat
東西進行檢查,但是由於檢查文件並創建它不是原子的,因此它無法解決任何問題。 而且我沒有找到原子操作的C ++ STL方法,該方法將:檢查文件是否存在,如果不存在,則將其打開,如果是,則失敗。
所以,我的問題是,解決這個問題的正確方法是什么? 我錯過了什么?
這是我幾乎沒有混亂的解決方案:
#include <cstdio>
#include <iostream>
#include <string>
#include <ext/stdio_filebuf.h> // libstdc++ specific
int main()
{
__gnu_cxx::stdio_filebuf<char> tmpfile_buf{std::tmpfile(), std::ios::in | std::ios::out | std::ios::binary};
std::iostream tmpstream{&tmpfile_buf};
// write in stream
tmpstream << "Hello World" << std::endl;
tmpstream.seekg(0);
// read from stream
std::string str;
std::getline(tmpstream, str);
std::cout << str << std::endl;
}
std::tmpfile()
並從具有基礎FILE*
的緩沖區構建流。 這是GNU擴展,因此不可移植:(。
您的規范“ ...創建文件,寫入文件,使用該文件作為輸入來調用另一個程序,然后刪除該文件。” 與tmpfile()不兼容。 一方面,沒有(便攜式)方法可以從您從tmpfile()獲得的FILE指針中獲取文件名,其次,在POSIX平台上,tmpfile()通常會從目錄中刪除文件,然后再從tmpfile()返回(如果您不熟悉POSIX文件系統的語義,只要您的進程具有打開的文件描述符(即使從目錄中刪除該文件),該文件就存在,這意味着無法從文件系統訪問它。
鑒於此,您將必須使用某種tmpname()類型的方法,並找出一種防止兩個進程同時訪問它的方法(文件鎖,使用stat()檢查鏈接數等)。
或者更好的是,不要使用文件進行進程間通信。 對於最簡單的情況(?),請在父級中創建一個管道,然后將其連接到子級的stdin。
我可能有建議的Boost解決方案,但是我不能在這里進行測試。
使用從std::tmpfile()
返回的FILE*
,可以獲取文件描述符:
#include <cstdio>
FILE* const tmpfile = std::tmpfile();
const int fd = ::fileno(tmpfile);
在這個階段,使用boost::iostreams::file_descriptor_source
看起來一切都很好:
#include <boost/iostreams/code_converter.hpp>
#include <boost/iostreams/maped_file.hpp>
file_descriptor_source tmpstream(fd, boost::iostreams::close_handle);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.