【问题标题】:Advice on converting timestamp string in "HH:MM:SS.microseconds" format关于将时间戳字符串转换为“HH:MM:SS.microseconds”格式的建议
【发布时间】:2022-01-08 04:05:06
【问题描述】:

我得到了一个时间戳列表(假设我们有一个现成的std::vector<std::string>),格式为std::vector<std::string> = {"12:27:37.740002", "19:37:17.314002", "20:00:07.140902",...}。没有日期,没有时区。将这些字符串解析为某种 C++ 类型(std::chrono::time_point ?)以便稍后执行一些比较和排序的更好方法是什么。

例如:比较从"20:00:07.140902"解析的值和从"20:00:07.000000"解析的值。

C++17 没问题,但我不能使用任何第三方库(Boost、Date 等)。 保持微秒级精度至关重要。

【问题讨论】:

  • 我建议将它们解析为具有四个或更多成员的struct。第一个代表小时,第二个代表分钟,第三个代表秒,第四个代表秒的分数,直到所需的粒度。例如,"12:27:37.740002" 将被解析为 struct TimeStamp{12, 27, 37, 740002}。这将使比较更容易。
  • C++库中没有这种东西,你用的关键词是“任何第三方库”。这意味着这个家庭作业要求你自己实现所有的解析,逐个字符,对无效输入进行错误检查,使用逻辑和循环,并从头到尾完成所有工作。 C++ 中很少有事情可以简单地通过找到一个可以完成所有事情的标准 C++ 库函数来完成。

标签: c++ string parsing timestamp c++17


【解决方案1】:

您可以使用 C++ 标准库功能完全构建此功能。 要解析字符串,请使用 std::regex。 对于时间相关的数据类型,使用 std::chrono

例子:

#include <stdexcept>
#include <regex>
#include <chrono>
#include <iostream>

auto parse_to_timepoint(const std::string& input)
{
    // setup a regular expression to parse the input string
    // https://regex101.com/
    // each part between () is a group and will end up in the match
    // [0-2] will match any character from 0 to 2 etc..
    // [0-9]{6} will match exactly 6 digits
    static const std::regex rx{ "([0-2][0-9]):([0-5][0-9]):([0-5][0-9])\\.([0-9]{6})" };
    std::smatch match;

    if (!std::regex_search(input, match, rx))
    {
        throw std::invalid_argument("input string is not a valid time string");
    }

    // convert each matched group to the corresponding value
    // note match[0] is the complete matched string by the regular expression
    // we only need the groups which start at index 1
    const auto& hours = std::stoul(match[1]);
    const auto& minutes = std::stoul(match[2]);
    const auto& seconds = std::stoul(match[3]);
    const auto& microseconds = std::stoul(match[4]);
    
    // build up a duration
    std::chrono::high_resolution_clock::duration duration{};
    duration += std::chrono::hours(hours);
    duration += std::chrono::minutes(minutes);
    duration += std::chrono::seconds(seconds);
    duration += std::chrono::microseconds(microseconds);

    // then return a time_point (note this will not help you with correctly handling day boundaries)
    // since there is no date in the input string
    return std::chrono::high_resolution_clock::time_point{ duration };
}

int main()
{
    std::string input1{ "20:00:07.140902" };
    std::string input2{ "20:00:07.000000" };

    auto tp1 = parse_to_timepoint(input1);
    auto tp2 = parse_to_timepoint(input2);

    std::cout << "start time = " << ((tp1 < tp2) ? input1 : input2) << "\n";
    std::cout << "end time = " << ((tp1 >= tp2) ? input1 : input2) << "\n";

    return 0;
}

【讨论】:

  • 最好将结果存储在std::chrono::microseconds中。
【解决方案2】:

我不明白为什么这不起作用。使用std::chrono::from_stream将字符串解析成一个时间点,然后比较两个时间点即可。

但是,我现在一直在使用 Visual Studio 2022 17.0.2(社区版)进行尝试,但无法将字符串解析为 tp

Ted Lyngmo 的 this answer 谈到了在用亚秒解析秒时出现的一个错误(已在 VS2022 17.0.3 中修复)。我不得不说,尽管他的解决方案在我的 VS2022 中也对我不起作用。

无论如何,你可能想试一试。

#include <chrono>
#include <iomanip>  // boolalpha
#include <iostream>  // cout
#include <sstream>  // istringstream
#include <string>

auto parse_string_to_tp(const std::string& str)
{
    std::istringstream iss{ str };
    std::chrono::sys_time<std::chrono::microseconds> tp{};
    std::chrono::from_stream(iss, "%H:%M:%S", tp);  // or simply "%T"
    return tp;
}

int main()
{
    const std::string str1{ "12:27:37.740002" };
    const std::string str2{ "13:00:00.500000" };

    auto tp1{ parse_string_to_tp(str1) };
    auto tp2{ parse_string_to_tp(str2) };

    std::cout << "tp1 < tp2: " << std::boolalpha << (tp1 < tp2) << "\n";
    std::cout << "tp2 < tp1: " << std::boolalpha << (tp2 < tp1) << "\n";
}

编辑:如果你只使用持续时间而不是时间点,它就可以工作:

#include <chrono>
#include <iomanip>  // boolalpha
#include <iostream>  // cout
#include <sstream>  // istringstream
#include <string>

auto parse_string_to_duration(const std::string& str)
{
    std::istringstream iss{ str };
    std::chrono::microseconds d{};
    std::chrono::from_stream(iss, "%T", d);
    return d;
}

int main()
{
    const std::string str1{ "12:27:37.740002" };
    const std::string str2{ "23:39:48.500000" };

    auto d1{ parse_string_to_duration(str1) };
    auto d2{ parse_string_to_duration(str2) };

    std::cout << "d1 < d2: " << std::boolalpha << (d1 < d2) << "\n";
    std::cout << "d2 < d1: " << std::boolalpha << (d2 < d1) << "\n";
}

【讨论】:

  • 对了,没有日期信息的时候,不能形成time_point。但是您可以形成一个持续时间,您可以将其解释为“自午夜以来的时间”,您可以自己定义午夜。
  • 感谢@HowardHinnant,这很有道理。
  • 看起来 std::chrono::from_stream 需要 C++20 ?
  • 是的,需要 C++20。在 date.h 中有一个免费的、开源的、仅标题的预览:github.com/HowardHinnant/date。唯一显着的区别是事物在命名空间 date 而不是命名空间 std::chrono。
猜你喜欢
  • 1970-01-01
  • 2016-05-24
  • 2021-03-29
  • 1970-01-01
  • 1970-01-01
  • 2020-11-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多