如何在C++中解析和验证std :: string中的日期?

emu*_*m13 6 c++ string validation parsing date

我正在开展一个项目,我必须阅读一个日期,以确保它是一个有效的日期.例如,2月29日只是闰年的有效日期,或者6月31日不是有效日期,因此计算机将根据输入输出该信息.我的问题是,我无法弄清楚如何解析字符串,以便用户可以输入"05/11/1996"作为日期(例如),然后取出并将其放入单独的整数.我正在考虑尝试用while循环和字符串流做一些事情,但我有点卡住了.如果有人可以帮助我,我会非常感激.

Lih*_*ihO 13

可能的解决方案也可能基于strptime,但请注意,此函数仅验证日期是否来自间隔<1;31>和月份<1;12>,即"30/02/2013"仍然有效:

#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;
}
Run Code Online (Sandbox Code Playgroud) 但是,由于strptime并不总是可用,额外的验证会很好,这是你可以做的:
  1. 提取日,月,年
  2. struct tm
  3. 将其标准化
  4. 检查标准化日期是否仍与检索的日,月,年相同

即:

#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;
}
Run Code Online (Sandbox Code Playgroud)

用作:

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;
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下将输出,date is invalid因为归一化将检测29/02/2013已经归一化为01/03/2013.


Joe*_*Joe 6

另一种选择是std::get_time<iomanip>标头中使用(自C ++ 11起可用)。可以找到其用法的一个很好的例子这里

  • 您应该在答案中提供示例。 (2认同)

小智 5

如果格式与您的示例类似,则可以取出如下的整数:

int day, month, year;
sscanf(buffer, "%2d/%2d/%4d",
    &month,
    &day,
    &year);
Run Code Online (Sandbox Code Playgroud)

当然在缓冲区你有日期("05/11/1996")


seh*_*ehe 5

我更喜欢使用Boost DateTime:

在科利鲁看到它

#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";
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)