4

ワイド文字列として保存されている数百万の日付をブースト日付に変換する必要があります

次のコードが機能します。ただし、これは恐ろしいコンパイラ警告を生成し、効率的ではないようです。

より良い方法はありますか?

#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() は複雑で、文書化されておらず、推奨されていません)

4

2 に答える 2

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私が示したフォームを使用して新しいものを作成するときは、既存のロケールとファセットへのポインターを与えます。指定されたファセットは、指定されたロケールで同じタイプの既存のファセットを置き換えます。(そのため、使用されている他の date_input_facet を置き換えます)。
  • すべてのファセットは何らかの形でロケールに関連付けられているためstd::has_facet<Facet>(some_locale)、特定のロケールに特定のファセット タイプがあるかどうかを確認するために使用できます。
  • を実行すると、1 つのロケールのファセットを使用できますstd::use_facet<Facet>(some_locale).some_member...
  • date_input_facet には get 関数があり、次のように使用できます。

以下は基本的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);
于 2008-11-29T16:35:35.333 に答える
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"
于 2008-11-29T17:28:09.313 に答える