2008-11-29 3 views
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++ библиотека локаль, которая представляет собой совокупность граней. Фасет - это услуга, которая позволяет операторам потока обрабатывать определенный выбор для представления даты или времени или просто о чем-либо еще. Все варианты о разных вещах, каждый из которых связан с его собственной гранью, собираются вместе в локали.

Это решение было указано мне litb, который дал мне достаточную помощь для использования граней в моем производственном коде, что сделало его более быстрым и быстрым. Благодарю.

Существует excellent tutorial на местах и ​​грани Nathan Myers, которые разработали гранулы. У него легкий стиль, благодаря которому его учебник легко читается, хотя это продвинутый материал, и ваш мозг может повредить после первого прочтения - мой. Я предлагаю вам пойти туда сейчас. Для тех, кто просто хочет практических преобразований широких струн символов для повышения дат, в остальной части этого сообщения описывается минимальное значение, необходимое для его работы.


Вначале предлагается следующее простое решение, которое удаляет предупреждение компилятора. (Решение было отредактировано до того, как я получил его, чтобы принять его.) Похоже, он делает то же самое, преобразовывая широкие символы один за другим, но он избегает развязывания с помощью temp-строк и, следовательно, гораздо яснее. Мне очень нравится, что предупреждение компилятора пропало.

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

Производит следующий вывод:

 
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); 

Почему не добавить (* фаска) метод?(_Addfac() является сложным, недокументированным и устаревшим)

+0

для вашего теста, после каждого извлечения вы должны вызвать .clear() в потоке. потому что он мог установить флаги ошибок, что приведет к отмене дальнейших операций чтения/записи. – 2008-11-29 21:02:10

+0

так, лучше делать для (int k = 0; k <100000; k ++) {ss << ws; ss >> d2; ss.clear();/* clear возможно eof flag * /} – 2008-11-29 21:05:15

+0

Уже сделано, спасибо. – ravenspoint 2008-11-29 21:06:24

ответ

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, и каждый имеет уникальный идентификатор.
  • imbue новый локаль в поток, заменяющий его старый язык. Локаль потока будет использоваться для всех видов разбора и преобразования.
  • Когда вы создаете новый std::locale, используя форму, которую я показал, вы даете ей существующий язык и указатель на фасет. Данная грань заменит любую существующую грань одного и того же типа в указанной локали. (таким образом, он заменит любой другой используемый date_input_facet).
  • Все грани связаны с локалью так или иначе, так что вы можете использовать std::has_facet<Facet>(some_locale), чтобы проверить, имеет ли данный язык определенный тип грани.
  • Вы можете использовать фасет из одного региона, выполнив std::use_facet<Facet>(some_locale).some_member....
  • date_input_facet имеет функцию ГЭТ, которая может быть использована, как это:

Ниже в основном делается путем operator>> по повышающего :: 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

Вы можете использовать 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"