boost :: gregorian input_facet意外的resutls

时间:2015-10-16 17:44:39

标签: c++ boost boost-date-time

我有一个关于从格式化字符串中读取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标志是ON?

我尝试使用不同的格式和输入字符串,并且看起来任何类型的可能分隔符的每个混合都适合它,除非根本没有分隔符,如上例所示。 此外,既没有查看boost文档,也没有调查代码本身,这使我得到了解决方案。

*使用g ++编译(Ubuntu 4.8.2-19ubuntu1)4.8.2,提升版本1.55

1 个答案:

答案 0 :(得分:3)

是的,我同意这种行为很奇怪。

发生的事情是,解析器根本不会验证分隔符!代码来自boost::date_time::format_date_parser

enter image description here

相反,代码只是盲目地跳过输入字符,假设它是一个分隔符。这意味着在20081023 1中为格式规范中的-解析。

接下来,为02说明符(因此,2月)采用两位数(%m)。

最后,为3分隔符解析-。显然,所有字段都被视为可选字段,因此未指定的日期默认为1

很多这些事情让我觉得非常草率。我快要在这里写自己的解析。

样本

<强> Live On Coliru

#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