2010-10-05 16 views
5

consideri questo un po 'di un follow-up a this question. In sostanza, il C++ data/ora formattazione strutture sembrano essere irrimediabilmente rotto - tanto che per fare qualcosa di semplice come convertire una stringa di data/ora in un oggetto, è davvero di ricorrere a uno o Boost.Datetime buon vecchio C strftime/strptime strutture.C++: convertire la data/string tempo per tm struct

Il problema è che nessuna di queste soluzioni funzionano direttamente con le impostazioni C++ di localizzazione impregnati su un particolare oggetto iostream. Le strutture C utilizzano le impostazioni C/locale POSIX globali, mentre le strutture di I/O in Boost.Datetime sembrano ignorare iostream impostazioni locali del tutto, consentendo all'utente di impostare direttamente i nomi dei mesi, giorni della settimana, ecc, indipendentemente dal locale.

Quindi, volevo qualcosa che rispetterebbe le impostazioni locali impregnate su un particolare flusso I/O che mi permettesse di convertire una stringa in struct tm. Sembrava abbastanza facile, ma mi sono imbattuto in ostacoli dietro ogni angolo. Inizialmente, ho notato che alcune implementazioni dell'STL forniscono una funzione non standard std::time_get::get, quindi ho deciso di implementare qualcosa di simile. Fondamentalmente, avrei semplicemente scorrere sopra la stringa di formato e ogni volta che ha colpito una bandiera formato, vorrei utilizzare una delle strutture time_get (come get_monthname, get_weekday, get_year, ecc) per convertire la stringa di input in un struct tm. Questo sembra abbastanza facile, tranne che ciascuna di queste funzioni richiede un intervallo di iterazione esatto. Non è possibile convertire "Monday,", deve essere esattamente "Monday" oppure la conversione non riesce. Poiché gli iteratori devono essere istreambuf_iterator, non è possibile eseguire semplicemente la scansione in avanti, poiché ogni incremento modifica la posizione di acquisizione nel buffer di flusso. Quindi, in sostanza si deve iterare prima sul torrente, la copia di ogni personaggio in un'altra streambuffer, e poi quando si colpisce un delimitatore (come uno spazio o una virgola), utilizza il secondo streambuffer con le strutture time_get. È letteralmente come se i progettisti di C++ facessero di tutto per renderlo il più fastidioso possibile.

Quindi, c'è una soluzione più semplice? Cosa fanno la maggior parte dei programmatori C++ quando hanno bisogno di convertire una stringa data/ora in un oggetto? Dobbiamo solo usare le strutture C e perdere i vantaggi che derivano dalle diverse impostazioni locali permeate da diversi oggetti iostream?

+0

Avete presentato qualche bug contro la vostra libreria C++? Reclami come questo guidano il loro sviluppo. – Potatoswatter

+0

'std :: :: time_get GET' è definito da C++ 0x 22.4.5.1.1/6 ... Mi è capitato di avere la pagina aperta, ma suppongo che sia in C++ 03 troppo. – Potatoswatter

+0

Non è in C++ 03: è disponibile in alcune implementazioni ma non è standard. –

risposta

2

Boost utilizza le impostazioni internazionali standard (s) per impostazione predefinita; non c'è bisogno di bypassare qualsiasi cosa:

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

(Quei nomi di locale sono specifici per Windows, ovviamente.) Uscita:

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 
0

perché non utilizzare la libreria C? È quasi certamente disponibile nella tua implementazione, ed è ben debugato e testato.

Se ritenete che manchi qualche funzione, sicuramente è più facile fare funzione wrapper che fa il lavoro fuso orario desiderato.

+0

Se il programma è progettato attorno a localizzazioni in C++, suppongo che potrebbe essere scomodo tradurre in C locales ... anche se non vedo come potrebbe essere più che ottenere il nome da locale :: name() e passare a setlocale ()/newlocale() ... che potrebbe essere piuttosto lento, però. – Potatoswatter

+0

Dipende da come li sta usando. Chiamando 'std :: locale :: global()' si imposterà anche la locale C se l'oggetto locale dato ha un nome. Se sta imburrando stream individuali con impostazioni locali diverse, dovrà impostare e disinserire manualmente le impostazioni internazionali C ogni volta che ne usa uno (o almeno, ogni volta che ne usa uno per l'elaborazione della data). Sarebbe facile scrivere una classe sentinella per aiutare in questo, però. –

0

cerco sempre di usare le stringhe indipendenti di locale per serializzare i dati. Rende la vita molto più facile.