如何使用 C++ 日期库解析秒和亚秒之间没有小数点的字符串?

How to parse strings with no decimal point between seconds and subseconds using the C++ date library?

提问人:DeepBlue 提问时间:7/14/2023 最后编辑:DeepBlue 更新时间:7/17/2023 访问量:64

问:

我目前正在使用 Howard Hinnant 的外部日期库,并且在解析与日期时间相关的字符串时遇到问题。解析字符串是没有问题的,因为我能够指定秒和亚秒之间的小数点。解析字符串,如 but 不起作用。HH:mm:ssSSSHH:mm:ss,SSSHH:mm:ssSSS

在这里发现了一个类似的问题。但是,它没有回答如何处理这种情况的问题。我不想更改字符串以设置秒和亚秒之间,只是为了处理这种特殊情况,特别是因为这只是我必须处理的任意众多情况之一。std::numpunct.

具体来说,通过使用如下所述的标点符号分面,可以在解析具有秒和亚秒的字符串时设置自定义小数分隔符。但是,似乎不可能完全省略秒和亚秒之间的小数分隔符,因为 (a) 仅适用于字符类型,并且 (b) 在通过日期库解析时使用 null 终止符根本不起作用。date::parsestd::numpunct

因此我的问题:有没有办法解析像 via 和 这样的字符串?HH:mm:ssSSSdate::parse()std::numpunct

class PunctuationFacet
    : public std::numpunct<char>
{
public:
   PuncutationFacet(char numericPunctuation, size_t referenceCount = 0)
      : std::numpunct<char>(referenceCount)
      , _numericPunctuation(numericPunctuation)

protected:
    char do_decimal_point() const { return _numericPunctuation; }

private:
    char _numericPunctuation;

std::optional<uin64_t> parse(std::string_view value, char numericPunctuation)
{
    date::sys_time<std::chrono::milliseconds> timepoint;

    std::stringstream ss;
    ss.imbue(std::locale(ss.getloc(), new PunctuationFacet(numericPunctuation)));
    ss << value;
    ss >> date::parse("%H:%M:%S", timepoint);
    if (ss.fail()) [[unlikely]]
    {
        return std::nullopt;
    }
    return timepoint.time_since_epoch().count();
}

int main(int argumentCount, char **arguments)
{
    auto timestampDoesWork = parse("14:20:51,123", ',');
    auto timestampDoesNotWork = parse("14:20:51123", 0);
}
C++ 日期 日期、时间、 分析标准

评论

1赞 Some programmer dude 7/14/2023
这很容易,因为您确切地知道字符串有多长。这意味着您可以创建两个子字符串,一个用于部件,一个用于部件,并分别解析它们。HH:mm:ssHH:mm:ssSSS
0赞 DeepBlue 7/14/2023
感谢您的快速回答!可悲的是,这并不容易实现,因为我正在为一个必须处理所有潜在格式组合的项目编写一个非常灵活的解析接口。但是,我可以让它工作,但我总是必须检查格式中是否存在秒数。但我想如果没有其他办法,我可能别无选择
0赞 Howard Hinnant 7/15/2023
当我注意到@Someprogrammerdude已经说出我要说的话时,我正要回答:解析两个整秒数字并使用自定义解析代码处理重置。
0赞 user207421 7/17/2023
您不需要将它们分开。只需将整个“秒”字段解析为以毫秒为单位的值,然后将取和取模除以 1000,即可在解析分离出秒和毫秒值。

答:

0赞 Howard Hinnant 7/17/2023 #1

一些程序员在评论中回答了这个问题,但在几天没有正式答案之后,我认为是时候做一个了。

Howard Hinnant的日期库和C++ chrono都没有办法解析带有隐式小数点字符的秒。std::numpunct

在下面的问题评论中,可以看出秒中的位数可以从 0 到某个尚未指定的数字不等。而这个答案并没有解决这种复杂性。

但是,对于未来的读者,以下是使用 Howard Hinnant 的日期库将表单的持续时间解析为的方法:HH:mm:ssSSSstd::chrono::milliseconds

#include "date/date.h"
#include <iostream>
#include <sstream>
#include <string>

// parse HH:mm:ssSSS
std::chrono::milliseconds
my_parse(std::string t)
{
    std::istringstream in{std::move(t)};
    in.exceptions(std::ios::failbit);
    int ms;
    std::chrono::seconds s;
    in >> date::parse("%T", s) >> ms;
    return s + std::chrono::milliseconds{ms};
}

int
main()
{
    using date::operator<<;
    std::cout << my_parse("14:20:51123") << '\n';
}

输出:

51651123ms

这将通过更改为 轻松移植到 C++20。date::parsestd::chrono::parse

诀窍是先解析成,使用(或等效的),然后将剩余的数字解析为 an 并将其转换为 .使用固定数量的数字,这很容易。seconds%T%H:%M:%Sintintstd::chrono::milliseconds

对于可变数量的数字,人们必须计算数字并将它们解释到适当的精度。当然是可行的,但需要的代码比这里显示的要多,并且更倾向于“你必须自己做”。