2013-10-20 11 views
6

Sto lavorando a un progetto in cui devo leggere una data per assicurarmi che sia una data valida. Ad esempio, il 29 febbraio è solo una data valida negli anni bisestili o il 31 giugno non è una data valida, quindi il computer produrrebbe tali informazioni in base all'input. Il mio problema è che non riesco a capire come analizzare la stringa in modo che l'utente possa inserire "05/11/1996" come una data (ad esempio) e quindi prenderlo e inserirlo in numeri interi separati. Stavo pensando di provare a fare qualcosa con un ciclo while e un flusso di stringhe, ma sono un po 'bloccato. Se qualcuno potesse aiutarmi con questo, lo apprezzerei davvero.Come analizzare e convalidare una data in std :: string in C++?

risposta

5

Se il formato è come nel tuo esempio, si potrebbe prendere l'intero in questo modo:

int day, month, year; 
sscanf(buffer, "%2d/%2d/%4d", 
    &month, 
    &day, 
    &year); 

in cui, naturalmente, in un tampone si ha la data ("1996/05/11")

+0

c'è un modo che questo possa funzionare con una stringa? Metto questo in un codice e impostando un buffer variabile come "05/20/13", sfortunatamente sta dicendo che non esiste una conversione adeguata tra string e const char. – emufossum13

+0

Sì, se si utilizza una std :: string, di buffer.c_str() è ciò che vi serve. – DrM

+0

Ecco come ho impostato la decodifica variabile std :: string buffer = buffer.c_str(); buffer = "05/10/1996"; è quello di cui stai parlando? – emufossum13

11

Una possibile soluzione potrebbe essere basata anche su strptime, tuttavia notare che questa funzione convalida solo se il giorno è dall'intervallo <1;31> e mese dalla <1;12>, cioè "30/02/2013" vale ancora:

#include <iostream> 
#include <ctime> 

int main() { 
    struct tm tm; 
    std::string s("32/02/2013"); 
    if (strptime(s.c_str(), "%d/%m/%Y", &tm)) 
     std::cout << "date is valid" << std::endl; 
    else 
     std::cout << "date is invalid" << std::endl; 
} 
Ma poiché strptime non è sempre disponibile e un'ulteriore convalida sarebbe bello, ecco cosa si potrebbe fare:
  1. estratto giorno, mese, anno
  2. riempire struct tm
  3. normalizzarlo
  4. verifica se la data normalizzata è ancora lo stesso giorno recuperate, mese, anno

cioè:

#include <iostream> 
#include <sstream> 
#include <ctime> 

// function expects the string in format dd/mm/yyyy: 
bool extractDate(const std::string& s, int& d, int& m, int& y){ 
    std::istringstream is(s); 
    char delimiter; 
    if (is >> d >> delimiter >> m >> delimiter >> y) { 
     struct tm t = {0}; 
     t.tm_mday = d; 
     t.tm_mon = m - 1; 
     t.tm_year = y - 1900; 
     t.tm_isdst = -1; 

     // normalize: 
     time_t when = mktime(&t); 
     const struct tm *norm = localtime(&when); 
     // the actual date would be: 
     // m = norm->tm_mon + 1; 
     // d = norm->tm_mday; 
     // y = norm->tm_year; 
     // e.g. 29/02/2013 would become 01/03/2013 

     // validate (is the normalized date still the same?): 
     return (norm->tm_mday == d && 
       norm->tm_mon == m - 1 && 
       norm->tm_year == y - 1900); 
    } 
    return false; 
} 

utilizzati come:

int main() { 

    std::string s("29/02/2013"); 
    int d,m,y; 

    if (extractDate(s, d, m, y)) 
     std::cout << "date " 
        << d << "/" << m << "/" << y 
        << " is valid" << std::endl; 
    else 
     std::cout << "date is invalid" << std::endl; 
} 

che in questo caso sarebbe uscita dal date is invalid normalizzazione rileverebbe che 29/02/2013 è stata normalizzata per 01/03/2013.

+0

bella risposta. La ricerca su Google mi ha portato qui quando cercavo di trovare un modo per convalidare una data inserita in C. Il tuo metodo gestisce anche gli anni bisestili e altri casi limite che sono grandiosi. – DaV

+0

C'è un problema: se si immette un anno inferiore al 1970 (epoca epoca), verrà normalizzato fino al 1970 e la funzione non riuscirà –

5

preferisco usare Boost DateTime:

vederlo Live on Coliru

#include <iostream> 
#include <boost/date_time/local_time/local_time.hpp> 

struct dateparser 
{ 
    dateparser(std::string fmt) 
    { 
     // set format 
     using namespace boost::local_time; 
     local_time_input_facet* input_facet = new local_time_input_facet(); 
     input_facet->format(fmt.c_str()); 
     ss.imbue(std::locale(ss.getloc(), input_facet)); 
    } 

    bool operator()(std::string const& text) 
    { 
     ss.clear(); 
     ss.str(text); 

     bool ok = ss >> pt; 

     if (ok) 
     { 
      auto tm = to_tm(pt); 
      year = tm.tm_year; 
      month = tm.tm_mon + 1; // for 1-based (1:jan, .. 12:dec) 
      day  = tm.tm_mday; 
     } 

     return ok; 
    } 

    boost::posix_time::ptime pt; 
    unsigned year, month, day; 

    private: 
    std::stringstream ss; 
}; 

int main(){ 
    dateparser parser("%d/%m/%Y"); // not thread safe 

    // parse 
    for (auto&& txt : { "05/11/1996", "30/02/1983", "29/02/2000", "29/02/2001" }) 
    { 
     if (parser(txt)) 
      std::cout << txt << " -> " << parser.pt << " is the " 
       << parser.day  << "th of " 
       << std::setw(2) << std::setfill('0') << parser.month 
       << " in the year " << parser.year  << "\n"; 
     else 
      std::cout << txt << " is not a valid date\n"; 
    } 
} 

Uscite:

05/11/1996 -> 1996-Nov-05 00:00:00 is the 5th of 11 in the year 96 
30/02/1983 is not a valid date 
29/02/2000 -> 2000-Feb-29 00:00:00 is the 29th of 02 in the year 100 
29/02/2001 is not a valid date 
+0

Ho appena refactored la mia risposta per mostrare (a) come riutilizzare in modo efficiente il flusso imbastito per parsing (b) che convalida gli input – sehe

3

Un'altra opzione è quella di utilizzare std::get_time dalla testata <iomanip> (disponibile dal C++ 11). Un buon esempio del suo utilizzo può essere trovato here.

Problemi correlati