ワイド文字列として保存されている数百万の日付をブースト日付に変換する必要があります
次のコードが機能します。ただし、これは恐ろしいコンパイラ警告を生成し、効率的ではないようです。
より良い方法はありますか?
#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を使用することです。ファセットとは、ストリーム オペレータが日付や時刻の表現などの特定の選択を処理できるようにするサービスです。それぞれが独自の側面で処理される、さまざまなことに関するすべての選択肢が、1 つのロケールにまとめられています。
このソリューションは、litb から指摘されました。litb は、本番コードでファセットを使用するのに十分な助けをしてくれて、コードをより簡潔かつ高速にしました。ありがとう。
ファセットを設計した Nathan Myers によるロケールとファセットに関する優れたチュートリアルがあります。彼はチュートリアルを読みやすくする軽いスタイルを持っていますが、これは高度な内容であり、最初に読んだ後に脳が痛む可能性があります. 今すぐそこに行くことをお勧めします。ワイド文字列をブースト日付に変換する実用性だけを求める人のために、この投稿の残りの部分では、それを機能させるために最低限必要なことについて説明します。
litb は、コンパイラの警告を削除する次の簡単なソリューションを最初に提供しました。(解決策は、私が受け入れる前に編集されました。)これは、ワイド文字を 1 つずつ変換して同じことを行うように見えますが、一時文字列をいじるのを避けるため、はるかに明確だと思います。コンパイラの警告がなくなったことを本当に気に入っています。
#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番目の方法: 2.422 3番目の方法: 1.968
OK、これは現在、製品コードに含まれており、回帰テストに合格しています。次のようになります。
// .. 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
これについて最後に 1 つ質問があります。ロケールにファセットを追加するには、ロケール コピー コンストラクターを 2 回呼び出す必要があるようです
// add facet to locale
greek_locale = locale( greek_locale, &greek_date_facet );
add( facet* ) メソッドがないのはなぜですか? ( _Addfac() は複雑で、文書化されておらず、推奨されていません)