C++如何跨平台线程安全地按用户区域偏好格式化文件最后修改时间
问题分析
你的代码存在3个核心错误,分别对应两个异常现象:
- Windows静默崩溃原因:你将栈上分配的
boost::gregorian::date_facet对象指针绑定到了locale中,boost和标准库的locale机制会默认接管传入的facet的生命周期,在locale销毁时自动调用delete释放该指针。栈内存被delete属于典型的未定义行为,会直接触发崩溃。另外Windows部分精简环境下std::locale("")构造如果找不到对应系统区域配置也会直接抛异常,但你的代码已经包了try/catch,所以核心崩溃原因是栈facet被误释放。 - Linux输出时间戳不符合预期原因:首先
boost::gregorian::date_facet仅能处理boost::gregorian::date类型的输出,你往流里输入的是boost::chrono::system_clock::time_point类型,该facet完全不生效;其次boost chrono的time_point默认输出逻辑就是打印从1970年纪元开始的时长,所以你看到的是纳秒数,你的格式配置从头到尾都没有生效。 - 额外错误:你连续两次调用
imbue,第二次的locale会直接覆盖第一次的用户偏好locale配置,等于你之前设置的系统默认locale直接失效了。
正确实现
直接用C++11原生支持的std::put_time即可满足需求,不需要额外用boost的日期facet,既避免内存问题,也能完美适配系统本地化格式:
std::string text; text += filename; text = "Savegame: "+text+lf+"_________________"+lf; { text += "Saved on: "; const boost::filesystem::path file_name_path{filename}; const boost::filesystem::path save_dir_path{getSaveDir()}; const boost::filesystem::path full_file_path = boost::filesystem::absolute(file_name_path, save_dir_path); std::stringstream last_saved_string_stream{}; // 可以按需开启异常,不开启的话格式化失败返回空字符串更温和 // last_saved_string_stream.exceptions(std::ios_base::failbit); try { // 先获取文件最后修改时间 const std::time_t last_saved_time = boost::filesystem::last_write_time(full_file_path); // 转成本地时间结构体,用线程安全的转换接口 std::tm local_time{}; #ifdef _WIN32 localtime_s(&local_time, &last_saved_time); #else localtime_r(&last_saved_time, &local_time); #endif // 注入系统用户默认的locale last_saved_string_stream.imbue(std::locale("")); // %c就是系统默认的本地化完整日期时间格式,完全符合需求 last_saved_string_stream << std::put_time(&local_time, "%c"); } catch (const boost::filesystem::filesystem_error& fse) { VS_LOG_AND_FLUSH(fatal, (boost::format("Filesystem Error determining savegame's last saved date/time: %1%") % fse.what())); } catch (const std::exception& e) { VS_LOG_AND_FLUSH(fatal, (boost::format("General Error determining savegame's last saved date/time: %1%") % e.what())); } catch (...) { VS_LOG_AND_FLUSH(fatal, "Really bad error determining savegame's last saved date/time! What just happened??"); } text += last_saved_string_stream.str() + lf; }
实现说明
- 线程安全:Windows用
localtime_s、Linux用localtime_r替代不安全的localtime,避免多线程下的缓冲区竞争问题,满足线程安全要求。 - 跨平台兼容:条件编译适配Windows和POSIX系统的线程安全本地时间转换接口,boost filesystem本身跨平台,
std::put_time是C++11标准特性,所有主流编译器都支持。 - 本地化适配:
std::locale("")会自动加载当前操作系统用户配置的区域设置,%c格式符会自动按照区域设置输出对应的日期时间格式,美国地区显示月/日/年 上/下午时间,欧洲地区显示日/月/年 24小时制时间,完全符合预期。 - 内存安全:没有手动分配内存,所有对象都用RAII管理,不存在之前的栈内存误释放问题。
内容的提问来源于stack exchange,提问作者Stephen G Tuggy
相关产品推荐
相关产品推荐

