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,它是facets的集合。facet 是一项服务,它允许流操作员处理日期或时间表示或其他任何内容的特定选择。所有关于不同事物的选择,每一个都由它自己的方面处理,都聚集在一个语言环境中。

这个解决方案是由litb向我指出的, 他给了我足够的帮助来在我的生产代码中使用构面,使其更简洁、更快。谢谢。

Nathan Myers有一个关于语言环境和方面的优秀教程,他设计了方面。他有一种轻松的风格,使他的教程易于阅读,尽管这是高级的东西,第一次通读后你的大脑可能会受伤——我的就是这样。我建议你现在去那里。对于任何只想将宽字符串转换为提升日期的实用性的人,本文的其余部分描述了使其工作所需的最低限度。


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

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使用我显示的表单创建一个新的时,您给它一个现有的语言环境和一个指向 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);
于 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 回答