编号的唯一文件

Numbered unique files

本文关键字:文件 唯一 编号      更新时间:2024-04-28

我想创建唯一的文件,必要时在文件名后面加一个数字(类似于浏览器通常命名下载文件的方式(。

以下是我尝试做的,但使用目录(使用<filesystem>库(:

#include <filesystem>
namespace fs = std::filesystem;
template <typename... Args> std::string concatenate(Args&&... args) {
std::ostringstream sstr;
(sstr << ... << std::forward<Args>(args));
return sstr.str();
}
fs::path unique_dir(const fs::path &base, unsigned max_tries) {
if(fs::create_directories(base))
return base;
for(unsigned i = 1; i < max_tries; ++i) {
fs::path p = base;
p += concatenate('_', i);
if(fs::create_directory(p))
return p;
}
throw std::runtime_error("unique_dir: gave up");
}
int main() {
unique_dir("my_dir", 3); // creates my_dir
unique_dir("my_dir", 3); // creates my_dir_1
unique_dir("my_dir", 3); // creates my_dir_2
unique_dir("my_dir", 3); // throws
}

如何对文件执行同样的操作?

一些精度:

  • 它不需要高性能(它适用于代码中非常冷的部分(
  • 非跨平台是可以的,只要有Linux、Windows和Mac的变体
  • 我不想使用mkstemp类型的函数,因为它需要在文件名中放入一个非用户友好的id

提前谢谢。

对于目录,您正在检查目录的创建是否有效。对于文件,您可以通过检查特定路径是否指向现有文件来实现此效果,如果不是,则创建它:

fs::path unique_file(const fs::path &base, unsigned max_tries) {
if(!fs::exists(base)) {
std::ofstream ofs(base);
return base;
}
for(unsigned i = 1; i < max_tries; ++i) {
fs::path p = base;
p += concat() << '_' << i;
if(!fs::exists(p)) {
std::ofstream ofs(p);
return p;
}
}
throw std::runtime_error("unique_file: gave up");
}

这里是一个POSIX系统的解决方案,使用open():

#include <fcntl.h> // open()
#include <unistd.h> // close()
fs::path unique_file(const fs::path &base, unsigned max_tries) {
fs::path p(base);
for(unsigned i = 1; i <= max_tries; ++i) {
// O_CREAT | O_EXCL will create the file if it does not exist, and fail otherwise
// 0666 : Read + Modify permissions for everybody
int fd = open(p.c_str(), O_CREAT | O_EXCL, 0666);
if(fd != -1) {
if(close(fd) == -1) // We immediately close the file, which might be a waste
throw fs::filesystem_error("unique_file: POSIX close() error", p, std::error_code(errno, std::generic_category()));
return p;
}
// EEXIST is set if open() failed because the file already existed
if(errno != EEXIST)
throw fs::filesystem_error("unique_file: POSIX open() error", p, std::error_code(errno, std::generic_category()));
errno = 0;
p = base.parent_path() / base.stem();
p += concatenate('_', i);
p += base.extension();
}
throw std::runtime_error("unique_file: gave up");
}

Windows似乎提供了_sopen_s功能,该功能提供了相同的_O_CREAT | _O_EXCL标志组合。