转换宽字符串以提升日期

时间:2008-11-29 16:27:02

标签: c++ datetime boost localization facet

我需要将存储为宽字符串的数百万个日期转换为提升日期

以下代码有效。但是,它会产生一个可怕的编译器警告,并且效率似乎不高。

有更好的方法吗?

#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()是复杂的,未记录的并且已弃用)

2 个答案:

答案 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模板实现了一个方面。
  • Facets派生自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...
  • 使用来自一个区域设置的构面
  • 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);

答案 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"