推荐用 std::chrono 获取系统时间并转为 std::tm,再用 std::put_time 格式化为“年月日时分秒”字符串生成文件名,精确到秒或毫秒可避免重复;需注意流的宽字符支持或 locale 设置。

用 std::chrono 和 std::put_time 生成带日期时间的文件名
直接拼接年月日时分秒是最常用也最可靠的方式,避免重复的关键在于精确到秒(或毫秒)。C++11 起推荐用 std::chrono 获取系统时间,再转成 std::tm 供 std::put_time 格式化——比过时的 localtime + strftime 更类型安全。
注意:std::put_time 要求流支持宽字符或 locale 设置,若遇到编译错误(如“no match for operatorstd::ostringstream 并已 imbue 正确 locale:
std::ostringstream oss;
oss.imbue(std::locale("")); // 或 std::locale::classic()
oss << std::put_time(&tm, "%Y%m%d_%H%M%S");
- 格式串
"%Y%m%d_%H%M%S"输出类似20240522_143527,无分隔符、可排序、不易出错 - 不要用
%f(微秒)——std::put_time不支持;需毫秒级唯一性,得手动从std::chrono::system_clock::now()提取毫秒数再拼接 - Windows 下若 locale 为空导致
put_time失效,fallback 方案是用sprintf(需包含<cstdio>)
用 std::filesystem::exists 检查文件是否已存在(防覆盖)
仅靠时间戳不能 100% 避免冲突(比如同一秒内多次运行),尤其在高频率写入或测试循环中。必须加一层存在性校验。
- 先生成基础名(如
"log_20240522_143527.txt"),再用std::filesystem::exists(<code>filename) 判断 - 若存在,在末尾追加序号(如
"log_20240522_143527_1.txt"),递增直到文件名可用 - 注意:C++17 起才支持
std::filesystem,编译需加-std=c++17(GCC/Clang)或启用 C++17(MSVC) - 不建议用
fopen(filename, "r")替代——它不跨平台(Windows 对大小写不敏感但 Linux 敏感),且无法区分“不存在”和“无权限”
Windows 下注意 std::filesystem::path 的路径分隔符问题
生成的文件名只是后缀,完整路径拼接时容易出错。直接字符串拼接 "./logs/" + basename 在 Windows 上可能因反斜杠缺失导致创建失败(尤其当 basename 含非法字符时)。
立即学习“C++免费学习笔记(深入)”;
- 统一用
std::filesystem::path构造:std::filesystem::path dir("./logs"); auto full_path = dir / basename; -
operator/会自动适配平台分隔符(Linux →/,Windows →\) - 创建目录前务必调用
std::filesystem::create_directories(dir),否则写入会失败(即使路径字符串看起来合法) - 避免在文件名里用
:、*、?等 Windows 禁止字符——std::put_time生成的格式天然安全,但若手动拼接要格外小心
性能与线程安全:多线程写入时别共享同一个 std::time_t 变量
如果多个线程并发生成文件名,共用一个 std::time_t + localtime_r(或 gmtime_r)变量会导致数据竞争——localtime 返回的指针指向静态缓冲区。
- 每个线程应独立调用
std::chrono::system_clock::now(),再各自转换为std::tm - 转换时用线程局部存储(
thread_local std::tm tm_buf)或栈上变量,避免复用全局缓冲区 - 若追求极致唯一性(比如每毫秒多个文件),可在时间戳后附加线程 ID(
std::this_thread::get_id())或原子计数器(static std::atomic<int> counter{0}</int>) - 注意:
std::this_thread::get_id()返回的是实现定义类型,转字符串需用std::stringstream或std::hash,不可直接



















