转换宽字符串以提升日期 [英] Convert wide character strings to boost dates

查看:191
本文介绍了转换宽字符串以提升日期的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I need to convert several million dates stored as wide strings into boost dates

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

The following code works. However, it generates a horrible compiler warning and does not seem efficient.

有更好的方法吗?

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


The better way turns out to be to use the standard C++ library locale, which is a collection of facets. A facet is a service which allows the stream operators to handle a particular choice for date or time representation or just about anything else. All the choices about diferent things, each handled by its own facet, are gathered together in a locale.

这个解决方案是由 litb ,他给了我足够的帮助,在我的生产代码中使用facet,使它更简单,更快。谢谢。

This solution was pointed out to me by litb who gave me enough help to use facets in my production code, making it terser and faster. Thanks.

有一个优秀教程关于设计方面的Nathan Myers的地方和方面。他有一个轻型的风格,使他的教程容易阅读,虽然这是先进的东西,你的大脑可能会受到伤害,第一次阅读后,我的。我建议你现在去那里。对于任何想要转换宽字符串来提升日期的实用性的人,这篇文章的其余部分描述了使其工作的最低限度。

There is an excellent tutorial on locales and facets by Nathan Myers who designed facets. He has a light style which makes his tutorial easy to read, though this is advanced stuff and your brain may hurt after the first read through - mine did. I suggest you go there now. For anyone who just wants the practicalities of converting wide character strings to boost dates, the rest of this post describes the minimum necessary to make it work.

litb首先提供了以下简单的解决方案,可以删除编译器警告。 (解决方案在我接受之前被编辑了。)这看起来像是一样的,一个接一个地转换宽字符,但是它避免了使用temp字符串,因此更清楚。我真的很喜欢编译器警告消失了。

litb first offered the following simple solution that removes the compiler warning. ( The solution was edited before I got around to accepting it. ) This looks like it does the same thing, converting wide characters one by one, but it avoids mucking around with temp strings and therefore is much clearer, I think. I really like that the compiler warning is gone.

#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继续建议使用facet,我从未听说过他们似乎做了这个工作,在循环中产生了令人难以置信的简洁的代码,费用是在设置区域设置的序言中。


litb went on to suggest using "facets", which I had never heard of before. They seem to do the job, producing incredibly terse code inside the loop, at the cost of a prologue where the locale is set up.

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;

事实证明,效率也更高:

This, it turns out, is also more efficient:

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

这是现在在生产代码和通过回归测试。它看起来像这样:

OK, this is now in the production code and passing regression tests. It looks like this:

  //  .. 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

我有一个最后的问题这个。将面添加到语言环境似乎需要两次调用语言环境复制构造函数

I have one final question about this. Adding the facet to the locale seems to require two invocations of the locale copy constructor

    // add facet to locale
greek_locale = locale( greek_locale, &greek_date_facet );

为什么没有添加(facet *)方法? (_Addfac()是复杂的,无文档的,不推荐使用)

Why is there not an add( facet* ) method? ( _Addfac() is complex, undocumented and deprecated )

推荐答案

efotinis使用 from_stream

efotinis found a good way using from_stream .

我查看了 date_time 的手册,并发现它支持方面:

I've looked into the manual of date_time and found it supports facets:

#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;
    }
}

你也可以去。

我已经查看了日期方面的工作原理:

I've looked up how date facets work:


  • 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,可以这样使用:

  • The boost::date_time::date_input_facet template implements a facet.
  • Facets are derived from std::locale::facet and every one has an unique id.
  • You can imbue a new locale into a stream, replacing its old locale. The locale of a stream will be used for all sorts of parsing and conversions.
  • When you create a new std::locale using the form i showed, you give it an existing locale, and a pointer to facet. The given facet will replace any existing facet of the same type in the locale given. (so, it would replace any other date_input_facet used).
  • All facets are associated with the locale somehow, so that you can use std::has_facet<Facet>(some_locale) to check whether the given locale has some given facet type.
  • You can use a facet from one locale by doing std::use_facet<Facet>(some_locale).some_member... .
  • date_input_facet has a function get, which can be used like this:

以下是基本上由$ code> operator>> by boost :: date_type: p>

The below is essentially done by operator>> by 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);

这篇关于转换宽字符串以提升日期的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆