【问题标题】:Saving and loading std::chrono::time_point to file保存和加载 std::chrono::time_point 到文件
【发布时间】:2016-05-01 05:25:15
【问题描述】:

我试图简单地保存并重新加载文件中的当前时间。例如:

std::ifstream ifs(solar_system_cache_file,
        std::ios::in | std::ios::binary);

if (!ifs.is_open()) {
    return false;
}

std::chrono::system_clock::time_point cache_valid_time;
ifs >> cache_valid_time;

if (std::chrono::system_clock::now() < cache_valid_time) {
    std::cout << "Cache is VALID." << std::endl;
}
ifs.close();
return true;

std::ofstream ofs(solar_system_cache_file,
        std::ios::out | std::ios::binary);

if (!ofs.is_open())
    return;

ofs << std::chrono::system_clock::now() + 12h;
ofs.close();

这类事情并不复杂,但我已经四处寻找了几个小时,但找不到相关信息。有一些关于如何使用 duration_cast&lt;long, std::milli&gt; 进行投射的示例,但 std::chrono 非常复杂且难以导航(和消化)。

简而言之,我相信我需要将当前时间转换为long(或一些类似的大类型)并保存。反序列化时间时,我只需将其转换回time_point。这听起来很容易,但我做不到。

最后,简单地在fstream 中传递时间会产生通常的invalid operands to binary expression 错误。

感谢任何帮助,或链接到好的教程/指南。 谢谢

【问题讨论】:

  • 您希望序列化格式是人类可读的吗?
  • 不,它是二进制形式。我发布了我的解决方案作为答案,当然可以随意解决这个问题:)

标签: c++ c++11 c++14 fstream chrono


【解决方案1】:

如果您想要二进制序列化,请不要使用格式化文本例程来实现序列化。 :-]

时钟的time_point 只是一个算术类型的封装,它是自时钟时代以来经过的某个时间单位的周期数。您真正需要做的就是以二进制形式序列化该算术值:

保存

using namespace std::chrono_literals;
using clock_t = std::chrono::system_clock;

std::ofstream ofs{ solar_system_cache_file, std::ios::binary };
if (!ofs.is_open()) {
    return;
}

auto const cache_time = (clock_t::now() + 12h).time_since_epoch().count();
ofs.write(reinterpret_cast<char const*>(&cache_time), sizeof cache_time);
ofs.close();

加载中

std::ifstream ifs{ solar_system_cache_file, std::ios::binary };
if (!ifs.is_open()) {
    return false;
}

clock_t::rep file_time_rep;
if (!ifs.read(reinterpret_cast<char*>(&file_time_rep), sizeof file_time_rep)) {
    return false;
}
ifs.close();

clock_t::time_point const cache_valid_time{ clock_t::duration{ file_time_rep } };
std::time_t const file_time{ clock_t::to_time_t(cache_valid_time) };
std::cout << std::ctime(&file_time);

请注意,将openmode::in 传递给输入流并将openmode::out 传递给输出流是多余的。

另外,重要的是,请注意二进制序列化是不可移植的:

  1. std::chrono::system_clock::rep 的类型是实现定义的——唯一的要求是它是一个有符号的算术类型——因此可以从一个编译器/stdlib/build 配置更改为下一个。
  2. 即使您通过 std::chrono::duration_cast 控制表示类型,整数类型的字节顺序和浮点类型的表示作为一个整体也是特定于架构的。

因此,您只能依靠使用与序列化代码相同的架构/编译器/stdlib/config 构建的代码来反序列化数据。

【讨论】:

  • 此外,epoch 甚至可能在每次运行时都发生变化(对于system_clock,它实际上并没有,但不能保证)。
  • 很好的答案,谢谢。我对rep 感到很困惑,它应该代表什么?
  • @Socapex : rep 是用于保存自纪元以来经过的时间单位数的类型。在我的系统上,system_clock::periodstd::nanosystem_clock::replong long,因此序列化的值是 long long,其中包含自纪元以来经过的纳秒数。
  • @TC:由于system_clock需要与time_t有转换关系,那么如果epoch从run到run的变化,要么time_t的epoch也必须从run 以完全相同的方式运行,或者在system_clock 到/从time_t 之间转换的函数必须在一侧或两侧跟踪这个时代的变化。在实践中,每个system_clock(和每个time_t)都测量Unix时间:en.wikipedia.org/wiki/Unix_time
  • @ildjarn 非常感谢您花时间解释所有这些,以及一个很好的答案。不知何故,我忘记了转换为 char (暂时没有完成 c++ 序列化)。祝你有美好的一天!
【解决方案2】:

好吧,经过一番折腾,我终于搞定了。

答案是std::time_t,是朋友std::chrono::system_clock::from_time_tstd::chrono::system_clock::to_time_t

所以要保存,您只需将::now() 转换为time_t 并将其通过管道传输到您的文件中。要加载,请使用 from_time_t 执行反向操作。

保存

std::ofstream ofs(solar_system_cache_file,
        std::ios::out | std::ios::binary);

if (!ofs.is_open())
    return;

auto cache_time = std::chrono::system_clock::now() + 12h;
ofs << std::chrono::system_clock::to_time_t(cache_time);
ofs.close();

加载中

std::ifstream ifs(solar_system_cache_file,
        std::ios::in | std::ios::binary);

if (!ifs.is_open()) {
    return false;
}

std::time_t file_time;
ifs >> file_time;
std::cout << std::ctime(&file_time);

auto cache_valid_time = std::chrono::system_clock::from_time_t(file_time);
ifs.close();

毕竟很简单,干杯!

【讨论】:

  • 如果您对time_t(文本形式)的精度(通常为秒)感到满意,则可以。 ildjarn 的答案对于您的 system_clock 测量的任何单位都是精确的(在您的情况下为纳秒),并使用更紧凑的二进制形式。
猜你喜欢
  • 1970-01-01
  • 2020-10-13
  • 2013-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多