Portable generation of a new file name in a specified directory
How can I portably (I am mostly interested in windows and linux) generate a new file name in a specified directory, with specified filename prefix and suffix?
std::string UniqueName(std::string const& dir, std::string const& prefix,
std::string const& suffix);
Any suggestions to implem开发者_开发问答ent this function, with as little as possible explicit dependencies on specific platforms.
Be aware that doing this wrong is a security hole. There are tricks to exploit temporary(ish) files, and these can give Administrator access to the whole system, not just your app. See this for some advice.
A couple of ways to do this:
- Whenever possible, use library-provided functions instead of writing your own. For example, in Windows use
GetTempFileName
, on Linux usemkstemp
. - Use
boost::filesystem::unique_path
, which lets you reliably generate unique filenames according to a template you provide.
boost::filesystem
is scheduled to become a part of C++ TR2, which should be supported by almost all compilers in the future. Note that you must #define BOOST_FILESYSTEM_VERSION 3
(info), otherwise you’ll get an older version of boost::filesystem
that doesn’t support unique_path
.
You could generate a UUID to create unique names. See this link for a list of implementations in C++.
For a windows solution, Generate a guid and use it as the filename
Here is the code to generate the guid to get you started.
_TUCHAR *guidStr = 0x00;
GUID *pguid = 0x00;
pguid = new GUID;
CoCreateGuid(pguid);
// Convert the GUID to a string
UuidToString(pguid, &guidStr);
delete pguid;
The function, given the signature you have, is impossible to implement perfectly. With filesystems, you very often have the problem of race conditions:
- Your functions decides on a filename "foobar"
- Someone else creates the file "foobar"
- Your program can't open "foobar" because someone else in step 2 beat you to it.
POSIX machines have a function to get around this: mkstemp
. Unfortunately, there is no Windows equivalent to this. On Windows, use GetTempFileName
, which is closer to mktemp
, but use CREATE_NEW
when calling CreateFile
, which will cause CreateFile
to fail if the file exists. From there, you can implement a Windows version of mkstemp
.
untested code, but i use something very similar in a couple projects
std::string UniqueName(std::string const& dir = _T(""), std::string const& prefix = _T(""), std::string const& suffix = _T(""))
{
std::string retVal = _T("");
std::string sDirectory = boost::filesystem::temp_directory_path().string();
if ( ! dir.empty() )
sDirectory = dir;
std::string sFilesName = _T("%%%%-%%%%-%%%%-%%%%");
if ( ! prefix.empty() )
sFilesName = prefix;
boost::format fmter( _T("%s%s%s") );
std::string sModel = boost::str ( boost::format ( fmter
% sDirectory
% sFilesName
% suffix ) );
boost::filesystem::path temp = boost::filesystem::unique_path(sModel);
retVal = temp.string();
return retVal;
}
精彩评论