boost::gregorian input_facet неожиданные результаты

У меня есть вопрос относительно чтения объекта boost::gregorian::date из отформатированной строки. Когда входная строка имеет указанный формат, она работает как положено. Например, код ниже

std::string fmt = "%Y-%m-%d";
std::string date_str = "2008-10-23";
boost::gregorian::date date;

boost::gregorian::date_input_facet* i_facet(new  boost::gregorian::date_input_facet());
i_facet->format(fmt.c_str());
std::stringstream ss;
ss.exceptions(std::ios_base::failbit);
ss.imbue(std::locale(ss.getloc(), i_facet));
ss << date_str;

ss >> date;

std::cout << date << std::endl;

выдает правильный вывод.

2008-Oct-23

Однако, если формат не соответствует входной строке, потоковая передача строки в объект даты приводит к неверным результатам:

// all the code is the same except input string is as follows: std::string date_str = "20081023";

дает 2008-Feb-01,

Итак, вопрос в том, почему он выдает неправильные результаты, а не генерирует исключение, несмотря на то, что флаг failbit включен?

Я попытался немного поиграть с разными форматами и входными строками, и кажется, что любая смесь любых типов возможных разделителей подходит для этого, если нет разделителей вообще, как в примере выше. Кроме того, ни изучение дополнительной документации, ни изучение самого кода не привели меня к решению.

* Скомпилировано с g++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2, буст версия 1.55

1 ответ

Решение

Да, я согласен, что поведение странное.

Что происходит, так это то, что парсер вообще не проверяет символы-разделители! Код от boost::date_time::format_date_parser:

введите описание изображения здесь

Вместо этого код просто пропускает вводимый символ вслепую, предполагая, что это разделитель. Это означает, что в 200810231 анализируется для - в спецификации формата.

Далее две цифры (02) принимаются за %m спецификатор (итак, февраль).

Наконец, 3 анализируется для - разделитель. По-видимому, все поля считаются необязательными, и поэтому по умолчанию для неопределенного дня 1,

Многие из этих вещей кажутся мне очень неряшливыми. Я бы написал свой собственный анализ здесь, в один миг.

DEMO

Жить на Колиру

#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_match.hpp>
#include <iostream>

struct as_yyyy_mm_dd {
    boost::gregorian::date& _into;

    friend std::istream& operator>>(std::istream& is, as_yyyy_mm_dd&& manip) {
        using namespace boost::spirit::qi;

        unsigned short y,m,d;
        if (is >> match(
                    uint_parser<unsigned short, 10, 4, 4>() >> '-' >>
                    uint_parser<unsigned short, 10, 2, 2>() >> '-' >>
                    uint_parser<unsigned short, 10, 2, 2>(), 
                    y, m, d))
        {
            manip._into = { y, m, d };
        }

        return is;
    };
};

int main() {
    boost::gregorian::date date;

    for (auto input : { "20081023", "2008-10-23" })
    {
        std::cout << "Parsing: '" << input << "' ";
        std::stringstream ss(input);
        //ss.exceptions(std::ios_base::failbit);

        if (ss >> as_yyyy_mm_dd{ date })
            std::cout << "Parsed: " << date << std::endl;
        else
            std::cout << "Parse failed\n";
    }
}

Печать:

Parsing: '20081023' Parse failed
Parsing: '2008-10-23' Parsed: 2008-Oct-23
Другие вопросы по тегам