【问题标题】:Parse time format "DD/MM/YYYY at hh:mm:ss" & others using std::chrono::from_stream()使用 std::chrono::from_stream() 解析时间格式 "DD/MM/YYYY at hh:mm:ss" & others
【发布时间】:2021-12-11 23:58:40
【问题描述】:

我目前正在尝试解析日志文件中列出的有关实验开始时间的一些信息。读入文件后,使用<regex> 解析列标题、开始时间、测量间隔时间等重要信息。

我正在尝试使用std::chrono::from_stream(...) 函数将格式为“DD/MM/YYYY at hh:mm:ss”的字符串解析为std::chrono::time_point,字符串示例:

2021 年 8 月 3 日 09:37:25

目前我正在尝试使用以下函数来尝试此操作,该函数尝试从提供的字符串构造持续时间以进行解析和字符串进行解析,然后将其转换为 time_point 以便我可以控制使用的时钟:

#include <chrono>
#include <string>
#include <sstream>
#include <iostream>

using nano = std::chrono::duration<std::uint64_t, std::nano>;

template <typename Duration>
Duration TimeFormat(const std::string& str,
                    const std::string& fmt,
                    const Duration& default_val)
{
    Duration dur;
    std::stringstream ss{ str };
    std::chrono::from_stream(ss, fmt.c_str(), dur);

    /*
    from_stream sets the failbit of the input stream if it fails to parse
    any part of the input format string or if it receives any contradictory
    information.
    */
    if (ss.good())
    {
        std::cout << "Successful parse!" << std::endl;
        std::cout << dur.count() << std::endl;
        return dur;
    }
    else
    {
        std::cout << "Failed parse!" << std::endl;
        std::cout << dur.count() << std::endl;
        return default_val;
    }
}

int main()
{
    /*
    The file is already read in, and regex matches the correct line from the log file and a
    format pattern from a related config file.
    */
    
    /*
    Two different lines in the log file give:
    - str1 = test start time.
    - str2 = time between each measurement.
    */
    std::string str1("08/03/2021 at 09:37:25"), str2("00:00:05");
    std::string fmt1("%d/%m/%Y at %H:%M:%S"), fmt2("%H:%M:%S");

    auto test1 = TimeFormat<nano>(str1, fmt1, nano::zero());
    /*
    --> "Failed parse!" & test1.count() = 14757395258967641292
    A little research indicates that this is what VS initializes variables to
    in debug mode. If run in release mode test1.count() = 0 in my tests.
    */

    auto test2 = TimeFormat<nano>(str2, fmt2, nano::zero());
    /* 
    --> "Failed parse!" & test2.count() = 5000000000 (5 billion nanoseconds)
    Chose nanoseconds because it also has to handle windows file times which are measured
    relative to 01/01/1601 in hundreds of nanoseconds. Might be worth pointing out.
    What's weird is that it fails even though the value it reads is correct.
    */

    /*
    ... Convert to a time_point after this,
    e.g auto t1 = std::chrono::time_point<std::chrono::high_resolution_clock, nano>(test1);
    */
}

可以在here 找到 from_stream 的 MS 文档。在 from_stream 文档之后提供有关不同格式字符的详细信息。

【问题讨论】:

  • 需要注意的是:std::chrono::time_point&lt;std::chrono::high_resolution_clock, nano&gt;(test1)是没有意义的。高分辨率时钟没有明确定义的纪元,因此这种时钟中的时间点实际上并不代表任何特定的日期。仅对具有明确定义的纪元的时钟应用日期派生持续时间才有意义(因为该持续时间是自该纪元以来的时间)。如果从文本中获取的时间不是UTC时间,那么你需要弄清楚它是什么时间。

标签: c++ c++20 chrono visual-c++-2019


【解决方案1】:

ss.is_good()?

这是您问题中的 type-o 还是 Visual Studio std::lib 中的扩展?

我猜这是一个类型-o,你的意思是ss.good()...

good() member function 检查是否所有状态标志都关闭:

  • failbit
  • badbit
  • eofbit

eofbit 尤其通常并不意味着“错误”。它只是意味着解析到达了流的末尾。您将“流结束”解释为解析错误。

改为检查failbitbadbit。使用fail() member function 最容易做到这一点。

if (!ss.fail())
    ...

更新

知道为什么它仍然不会传递第一个字符串吗?

如果它是 VS 实现中的错误,或者 C++ 规范中的错误,或者两者都不是,我不是 100% 肯定的。无论哪种方式,它都不会返回您所期望的。

对我来说(使用C++20 chrono preview library),第一次解析成功并返回

34645000000000

如果以 hh:mm:ss.ffffffff 格式打印出来是:

09:37:25.000000000

也就是说,只有时间部分对返回值有贡献。这显然不是你想要的。您的第一个测试似乎打算解析 time_point,而不是 duration

这是一个稍微重写的程序,我认为它会做你想做的事,在第一个测试中解析 time_point,在第二个测试中解析 duration

#include <chrono>
#include <string>
#include <sstream>
#include <iostream>

using nano = std::chrono::duration<std::uint64_t, std::nano>;

template <typename TimeType>
TimeType TimeFormat(const std::string& str,
                    const std::string& fmt,
                    const TimeType& default_val)
{
    TimeType dur;
    std::stringstream ss{ str };
    std::chrono::from_stream(ss, fmt.c_str(), dur);

    /*
    from_stream sets the failbit of the input stream if it fails to parse
    any part of the input format string or if it receives any contradictory
    information.
    */
    if (!ss.fail())
    {
        std::cout << "Successful parse!" << std::endl;
        std::cout << dur << std::endl;
        return dur;
    }
    else
    {
        std::cout << "Failed parse!" << std::endl;
        std::cout << dur << std::endl;
        return default_val;
    }
}

int main()
{

    std::string str1("08/03/2021 at 09:37:25"), str2("00:00:05");
    std::string fmt1("%d/%m/%Y at %H:%M:%S"), fmt2("%H:%M:%S");

    auto test1 = TimeFormat(str1, fmt1, std::chrono::sys_time<nano>{});
    auto test2 = TimeFormat(str2, fmt2, nano::zero());
}

对我来说这个输出:

Successful parse!
2021-03-08 09:37:25.000000000
Successful parse!
5000000000ns

如果想要从纪元以来以纳秒为单位的第一个测试的输出,那么可以使用 dur.time_since_epoch()dur time_point 变量中提取它。这将输出:

1615196245000000000ns

【讨论】:

  • 或者只是if (ss),也可以是if (from_stream(...))
  • 是的。我有点喜欢更冗长的!fail(),但这是一种风格选择。
  • 很抱歉这么久才回复您。确实是笔误,谢谢指出。感谢您指出 good() 检查 eofbit,我没有意识到。知道为什么它仍然不会通过第一个字符串吗?
  • 感谢大家的帮助,我在这个问题上卡了多久有点尴尬。
猜你喜欢
  • 2017-07-09
  • 2014-12-26
  • 1970-01-01
  • 2015-10-08
  • 2013-03-21
  • 2020-03-07
  • 1970-01-01
  • 1970-01-01
  • 2022-11-16
相关资源
最近更新 更多