【发布时间】:2008-11-29 16:27:01
【问题描述】:
我需要将存储为宽字符串的数百万个日期转换为提升日期
以下代码有效。但是,它会生成可怕的编译器警告,而且似乎效率不高。
有没有更好的办法?
#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 的集合。 facet 是一项服务,它允许流操作员处理日期或时间表示或其他任何内容的特定选择。所有关于不同事物的选择,每一个都由它自己的方面处理,都聚集在一个语言环境中。
litb 向我指出了这个解决方案,他为我提供了足够的帮助来在我的生产代码中使用构面,使其更简洁、更快。谢谢。
设计构面的 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 继续建议使用我以前从未听说过的“构面”。他们似乎完成了这项工作,在循环中生成了极其简洁的代码,但代价是设置语言环境的序言。
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;
产生以下输出:
第一种方法:2.453 第二种方法:2.422 第三种方法: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() 很复杂,未记录且已弃用)
【问题讨论】:
-
对于您的测试,每次提取后,您应该在流上调用 .clear()。因为它可能设置了错误标志,这将导致进一步的读/写被取消。
-
所以,最好为(int k = 0; k > d2; ss.clear(); /* 清除可能的 eof 标志 */ }
标签: c++ datetime boost localization facet