boost::gregorian input_facet 意想不到的结果
boost::gregorian input_facet unexpected results
我有一个关于从格式化字符串中读取 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,boost版本1.55
是的,我同意这种行为很奇怪。
发生的事情是解析器根本不验证分隔符!来自 boost::date_time::format_date_parser
的代码:
相反,代码只是盲目地跳过输入字符,假设它是一个分隔符。这意味着在 20081023
中 1
被解析为格式规范中的 -
。
接下来,%m
说明符(so,Feb)取两位数字 (02
)。
最后,3
被解析为 -
分隔符。显然,所有字段都被视为可选字段,因此未指定的日期默认为 1
.
很多这样的事情让我觉得非常草率。我很快就会在这里编写自己的解析。
演示
#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
我有一个关于从格式化字符串中读取 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,boost版本1.55
是的,我同意这种行为很奇怪。
发生的事情是解析器根本不验证分隔符!来自 boost::date_time::format_date_parser
的代码:
相反,代码只是盲目地跳过输入字符,假设它是一个分隔符。这意味着在 20081023
中 1
被解析为格式规范中的 -
。
接下来,%m
说明符(so,Feb)取两位数字 (02
)。
最后,3
被解析为 -
分隔符。显然,所有字段都被视为可选字段,因此未指定的日期默认为 1
.
很多这样的事情让我觉得非常草率。我很快就会在这里编写自己的解析。
演示
#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