我需要将存储为宽字符串的数百万个日期转换为提升日期
以下代码有效。但是,它会产生一个可怕的编译器警告,并且效率似乎不高。
有更好的方法吗?
#include "boost/date_time/gregorian/gregorian.hpp"
using namespace boost::gregorian;
#include <string>
using namespace std;
wstring ws( L"2008/01/01" );
string temp(ws.length(), '\0');
copy(ws.begin(), ws.end(), temp.begin());
date d1( from_simple_string( temp ) );
cout << d1;
更好的方法是使用标准C ++库 locale ,它是 facets 的集合。方面是一种服务,它允许流操作员处理日期或时间表示的特定选择或其他任何事情。关于不同事物的所有选择,每个都由它自己的方面处理,在一个地方聚集在一起。
litb向我指出了这个解决方案,他给了我足够的帮助,在我的生产代码中使用了facet,使其更加简洁和快速。感谢。
Nathan Myers设计方面的区域和方面有excellent tutorial。他有一个轻松的风格,使他的教程易于阅读,虽然这是先进的东西,你的大脑可能会在第一次阅读后受伤 - 我的。我建议你现在去那儿。对于那些只想要将宽字符串转换为提升日期的实用性的人来说,本文的其余部分描述了使其工作的最低限度。
litb首先提供了以下简单的解决方案来删除编译器警告。 (解决方案在我开始接受之前进行了编辑。)这看起来像是做同样的事情,逐个转换宽字符,但它避免了使用临时字符串,因此我认为更清晰。我真的很喜欢编译器警告消失了。
#include "boost/date_time/gregorian/gregorian.hpp"
using namespace boost::gregorian;
#include <string>
using namespace std;
wstring ws( L"2008/01/01" );
date d1( from_simple_string( string( ws.begin(), ws.end() ) );
cout << d1;
litb继续建议使用我以前从未听说过的“facets”。他们似乎做了这项工作,在循环中产生令人难以置信的简洁代码,代价是设置语言环境的序幕。
wstring ws( L"2008/01/01" );
// construct a locale to collect all the particulars of the 'greek' style
locale greek_locale;
// construct a facet to handle greek dates - wide characters in 2008/Dec/31 format
wdate_input_facet greek_date_facet(L"%Y/%m/%d");
// add facet to locale
greek_locale = locale( greek_locale, &greek_date_facet );
// construct stringstream to use greek locale
std::wstringstream greek_ss;
greek_ss.imbue( greek_locale );
date d2;
greek_ss << ws;
greek_ss >> d2;
cout << d2;
事实证明,这也更有效:
clock_t start, finish;
double duration;
start = clock();
for( int k = 0; k < 100000; k++ ) {
string temp(ws.length(), '\0');
copy(ws.begin(), ws.end(), temp.begin());
date d1( from_simple_string( temp ) );
}
finish = clock();
duration = (double)(finish - start) / CLOCKS_PER_SEC;
cout << "1st method: " << duration << endl;
start = clock();
for( int k = 0; k < 100000; k++ ) {
date d1( from_simple_string( string( ws.begin(), ws.end() ) ) );
}
finish = clock();
duration = (double)(finish - start) / CLOCKS_PER_SEC;
cout << "2nd method: " << duration << endl;
start = clock();
for( int k = 0; k < 100000; k++ ) {
greek_ss << ws;
greek_ss >> d2;
ss.clear();
}
finish = clock();
duration = (double)(finish - start) / CLOCKS_PER_SEC;
cout << "3rd method: " << duration << endl;
产生以下输出:
1st method: 2.453 2nd method: 2.422 3rd method: 1.968
好的,现在这是生产代码并通过回归测试。它看起来像这样:
// .. construct greek locale and stringstream
// ... loop over input extracting date strings
// convert range to boost dates
date d1;
greek_ss<< sd1; greek_ss >> d1;
if( greek_ss.fail() ) {
// input is garbled
wcout << L"do not understand " << sl << endl;
exit(1);
}
greek_ss.clear();
// finish processing and end loop
我有一个关于此问题的最后一个问题。将构面添加到区域设置似乎需要两次调用区域设置复制构造函数
// add facet to locale
greek_locale = locale( greek_locale, &greek_date_facet );
为什么没有add(facet *)方法? (_Addfac()是复杂的,未记录的并且已弃用)
答案 0 :(得分:2)
efotinis使用 from_stream 找到了一个好方法。
我查看了date_time
的手册,发现它支持方面:
#include <boost/date_time/gregorian/gregorian.hpp>
#include <iostream>
#include <sstream>
#include <locale>
int main() {
using namespace boost::gregorian;
std::wstringstream ss;
wdate_input_facet * fac = new wdate_input_facet(L"%Y-%m-%d");
ss.imbue(std::locale(std::locale::classic(), fac));
date d;
ss << L"2004-01-01 2005-01-01 2006-06-06";
while(ss >> d) {
std::cout << d << std::endl;
}
}
你也可以这样做。
我已经查明了日期方面的工作原理:
boost::date_time::date_input_facet
模板实现了一个方面。 std::locale::facet
,每个人都有唯一的ID。std::locale
时,您将为其提供现有的区域设置和指向构面的指针。给定的facet将替换给定语言环境中相同类型的任何现有facet。 (因此,它将替换使用的任何其他date_input_facet。)std::has_facet<Facet>(some_locale)
检查给定区域设置是否具有某些给定方面类型。std::use_facet<Facet>(some_locale).some_member...
。以下内容基本上由operator>>
由boost :: date_type:
// assume src is a stream having the wdate_input_facet in its locale.
// wdate_input_facet is a boost::date_time::date_input_facet<date,wchar_t> typedef.
date d;
// iterate over characters of src
std::istreambuf_iterator<wchar_t> b(src), e;
// use the facet to parse the date
std::use_facet<wdate_input_facet>(src.getloc()).get(b, e, src, d);
答案 1 :(得分:1)
您可以使用 from_stream 解析器功能:
using boost::gregorian::date;
using boost::gregorian::from_stream;
std::wstring ws( L"2008/01/01" );
date d1(from_stream(ws.begin(), ws.end()));
std::cout << d1; // prints "2008-Jan-01"