Анализ продолжительности ISO 8601

В ISO 8601 длительность указывается в формате P[n]Y[n]M[n]DT[n]H[n]M[n]S.

Примеры:

20 секунд:

PT20.0S

Один год, 2 месяца, 3 дня, 4 часа, 5 минут, 6 секунд:

P1Y2M3DT4H5M6S

Вопрос:

Дана строка, содержащая продолжительность в формате iso 8601. Я хочу получить общее количество секунд этой продолжительности. Каким способом в стандарте C ++ 11 рекомендуется достичь этого?

Примечания:

Например, в boost DateTime есть ptime from_iso_string (std :: string), который здесь не подходит. Есть ли аналогичный способ без регулярного выражения вручную?


person SebastianK    schedule 27.05.2014    source источник
comment
Если у вас нет регулярного выражения, почему вы просите специально для стандартного C ++ 11? Regex является частью стандарта C ++ 11 - вы, скорее всего, ищете решение, совместимое с C ++ 03.   -  person Constantin    schedule 27.05.2014
comment
Вы правы, но меня интересует и то, и другое: как это сделать правильно (используя C ++ 11) и как это сделать «на данный момент». Я также думаю, что решение с регулярным выражением - это кодирование вручную. Но поскольку это стандарт, возможно, существует уже существующий инструмент, который можно было бы использовать.   -  person SebastianK    schedule 27.05.2014


Ответы (2)


Используйте стандартную библиотеку регулярных выражений, желаемое регулярное выражение выглядит примерно так:

"P\(\([0-9]+\)Y\)?\(\([0-9]+\)M\)?\(\([0-9]+\)D\)?T\(\([0-9]+\)H\)?\(\([0-9]+\)M\)?\(\([0-9]+\(\.[0-9]+\)?S\)?"

отсюда вы можете выделить количество лет, месяцев и т. д. и вычислить общее количество секунд.

person Paul Evans    schedule 27.05.2014
comment
Спасибо, к сожалению, я использую gcc 4.8, где стандартная библиотека регулярных выражений еще не работает должным образом. Обновление до gcc 4.9 было бы затруднительным, поскольку моя среда основана на QT, который поставляется с gcc 4.8 в последней версии ... - person SebastianK; 27.05.2014
comment
Затем используйте Boost.Regex. или библиотеку Boost.Xpressive. - person Paul Evans; 27.05.2014
comment
Спасибо. Однако мне было интересно, есть ли стандартное решение без ручного кодирования регулярного выражения. Казалось, что это обычная проблема, например в boost DateTime есть ptime from_iso_string (std :: string), но это не подходит. - person SebastianK; 27.05.2014

Пример кода для преобразователя длительности ISO 8601 в эпоху Unix:

#include <iostream>
#include <vector>
#include <regex>

using namespace std;

void match_duration(const std::string& input, const std::regex& re)
{
    std::smatch match;
    std::regex_search(input, match, re);
    if (match.empty()) {
        std::cout << "Pattern do NOT match" << std::endl;
        return;
    }

    std::vector<double> vec = {0,0,0,0,0,0}; // years, months, days, hours, minutes, seconds

    for (size_t i = 1; i < match.size(); ++i) {

        if (match[i].matched) {
            std::string str = match[i];
            str.pop_back(); // remove last character.
            vec[i-1] = std::stod(str);
        }
    }

    int duration = 31556926   * vec[0] +  // years  
                   2629743.83 * vec[1] +  // months
                   86400      * vec[2] +  // days
                   3600       * vec[3] +  // hours
                   60         * vec[4] +  // minutes
                   1          * vec[5];   // seconds

    if (duration == 0) {
        std::cout << "Not valid input" << std::endl;
        return;
    }

    std::cout << "duration: " << duration << " [sec.]" << std::endl;
}

int main()
{
    std::cout << "-- ISO 8601 duration to Unix epoch time converter--" << std::endl;
    std::cout << "Enter duration (q for quit)" << std::endl;

    std::string input;
    //input = "P1Y2M3DT4H5M6S";
    //input = "PT4H5M6S";
    //
    while(true)
    {
        std::cin >> input;
        if (!std::cin)
            break;
        if (input == "q")
            break;

        std::regex rshort("^((?!T).)*$");

        if (std::regex_match(input, rshort)) // no T (Time) exist
        {
            std::regex r("P([[:d:]]+Y)?([[:d:]]+M)?([[:d:]]+D)?");
            match_duration(input, r);
        }
        else {

            std::regex r("P([[:d:]]+Y)?([[:d:]]+M)?([[:d:]]+D)?T([[:d:]]+H)?([[:d:]]+M)?([[:d:]]+S|[[:d:]]+\\.[[:d:]]+S)?");
            match_duration(input, r);
        }
    }

    return 0;
  }
person sigidagi    schedule 30.12.2015
comment
собирались ли вы завершить эту мысль правкой? - person Drew; 30.12.2015
comment
@Drew вы, вероятно, имеете в виду «когда» вместо «где». Теперь я это сделал. - person sigidagi; 30.12.2015