简体   繁体   中英

Parse time format "DD/MM/YYYY at hh:mm:ss" & others using std::chrono::from_stream()

I'm currently trying to parse some info about the start time of an experiment as listed in a log file. After reading in the file important info, eg column titles, start time, time between measurements, is parsed using <regex> .

I'm trying to use the std::chrono::from_stream(...) function to parse a string with the format "DD/MM/YYYY at hh:mm:ss" into a std::chrono::time_point , example of a string:

08/03/2021 at 09:37:25

At the moment I'm attempting this using the following function which attempts to construct a duration from a provided string to parse & a string to parse it with, then converting that to a time_point so I have control over the clock used:

#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.is_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);
    */
}

The MS documentation for from_stream can be found here . With details about different format characters just after the from_stream docs.

ss.is_good() ?

Is that a type-o in your question or an extension in the Visual Studio std::lib?

I'm going to guess it is a type-o and that you meant ss.good() ...

The good() member function checks if all state flags are off:

  • failbit
  • badbit
  • eofbit

eofbit in particular often does not mean "error". It simply means that the parsing reached the end of the stream. You are interpreting "end of stream" as a parsing error.

Instead check failbit or badbit . This is most easily done with the fail() member function .

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM