C ++: преобразовать строку даты / времени в структуру tm

Считайте это своего рода продолжением этого вопроса . По сути, средства форматирования даты / времени C ++ кажутся безнадежно сломанными - настолько, что для того, чтобы сделать что-то столь же простое, как преобразование строки даты / времени в объект, вам действительно нужно прибегнуть либо к Boost.Datetime, либо к старому доброму C _1 _ / _ 2_ объектов.

Проблема в том, что ни одно из этих решений не работает напрямую с настройками локали C ++, встроенными в конкретный объект iostream. Средства C используют глобальные настройки языкового стандарта C / POSIX, тогда как средства ввода-вывода в Boost.Datetime, похоже, полностью обходят настройки языкового стандарта iostream, позволяя пользователю напрямую устанавливать названия месяцев, дней недели и т. Д., Независимо от языкового стандарта.

Итак, мне нужно было что-то, что учитывало бы настройки локали, встроенные в конкретный поток ввода-вывода, что позволило бы мне преобразовать строку в struct tm. Это казалось достаточно простым, но я натыкался на препятствия на каждом углу. Сначала я заметил, что некоторые реализации STL предоставляют нестандартную функцию std::time_get::get, поэтому я решил реализовать нечто подобное. По сути, я просто перебираю строку формата и всякий раз, когда нажимаю флаг формата, я использую одно из средств time_get (например, get_monthname, get_weekday, get_year и т. Д.) Для преобразования входной строки в struct tm. Это кажется достаточно простым, за исключением того, что для каждой из этих функций требуется точный диапазон итератора. Вы не можете преобразовать "Monday,", это должно быть "Monday" в точности, иначе преобразование завершится неудачно. Поскольку итераторы должны быть istreambuf_iterator, вы не можете просто сканировать вперед, потому что каждое приращение изменяет позицию получения в буфере потока. Итак, в основном вам нужно сначала выполнить итерацию по потоку, скопировав каждый символ в другой потоковый буфер, а затем, когда вы нажмете разделитель (например, пробел или запятую), используйте второй потоковый буфер с функциями time_get . Буквально, как если бы дизайнеры C ++ изо всех сил старались сделать это как можно более раздражающим.

Итак, есть ли более простое решение? Что делают большинство программистов на C ++, когда им нужно преобразовать строку даты / времени в объект? Неужели мы просто должны использовать возможности C и терять преимущества, связанные с разными настройками локали, присущими разным объектам iostream?


person Charles Salvia    schedule 05.10.2010    source источник
comment
Вы сообщали об ошибках в своей библиотеке C ++? Подобные жалобы стимулируют их развитие.   -  person Potatoswatter    schedule 05.10.2010
comment
std::time_get::get определяется C ++ 0x 22.4.5.1.1 / 6… Я просто случайно открыл страницу, но я предполагаю, что она и на C ++ 03.   -  person Potatoswatter    schedule 05.10.2010
comment
Его нет в C ++ 03: он доступен в некоторых реализациях, но не является стандартным.   -  person Charles Salvia    schedule 05.10.2010
comment
У меня есть решение, но оно использует ускорение. Работает нормально, разбирает дату в любом нужном вам формате.   -  person zabulus    schedule 11.10.2010


Ответы (3)


Boost по умолчанию использует стандартные языковые стандарты; вам не нужно ничего обходить:

#include "boost/date_time/gregorian/gregorian.hpp"
#include <iostream>
#include <sstream>
#include <ctime>

int main(){
  using namespace boost::gregorian;

  std::locale::global(std::locale(""));
  std::locale german("German_Germany");
  std::locale french("French_France");

  date d1(day_clock::local_day());
  date d2;
  std::stringstream ss("2002-May-01");

  std::cout << "Mine: " << d1 << " | ";
  ss >> d2;
  std::cout << d2 << '\n';

  std::cout.imbue(german);
  std::cout << "Germany: " << d1 << " | ";
  ss.imbue(german);
  ss << "2002-Mai-01";
  ss >> d2;
  std::cout << d2 << '\n';

  std::cout.imbue(french);
  std::cout << "France: " << d1 << " | " << d2 << '\n';

  std::tm t = to_tm(d1);
  std::cout << "tm: " << asctime(&t);
}

(Эти имена локалей, конечно, специфичны для Windows.) Вывод:

Mine: 2010-Oct-28 | 2002-May-01
Germany: 2010-Okt-28 | 2002-Mai-01
France: 2010-oct.-28 | 2002-mai-01
tm: Thu Oct 28 00:00:00 2010
person Steve M    schedule 28.10.2010

Почему бы не использовать библиотеку C? Он почти наверняка доступен в вашей реализации, хорошо отлажен и протестирован.

Если ему не хватает какой-то функции, конечно, проще сделать функцию-оболочку, которая выполняет работу с часовым поясом, который вы хотите.

person wallyk    schedule 05.10.2010
comment
Если программа разработана для языковых стандартов C ++, я полагаю, может быть неудобно переводить обратно на локали C ... хотя я не понимаю, как это может быть чем-то большим, чем получение имени из locale :: name () и переход к setlocale () / newlocale ()… это может быть довольно медленным. - person Potatoswatter; 05.10.2010
comment
Это зависит от того, как он их использует. Вызов std::locale::global() также установит языковой стандарт C, если данный объект языкового стандарта имеет имя. Если он наполняет отдельные потоки разными языковыми стандартами, ему придется вручную устанавливать и отключать языковой стандарт C каждый раз, когда он использует один из них (или, по крайней мере, каждый раз, когда он использует один для обработки даты). Однако было бы легко написать класс дозорного, чтобы помочь с этим. - person Steve M; 28.10.2010

Я всегда стараюсь использовать независимые от локали строки для сериализации данных. Делает жизнь намного проще.

person Dialecticus    schedule 12.10.2010