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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19482378/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 22:53:31  来源:igfitidea点击:

How to parse and validate a date in std::string in C++?

c++stringvalidationparsingdate

提问by emufossum13

I'm working on a project where I have to read a date to make sure that it's a valid date. For example, February 29th is only a valid date on leap years, or June 31st is not a valid date, so the computer would output that information based on the input. My issue is that I can't figure out how to parse the string so that the user can enter "05/11/1996" as a date (for example) and then take that and put it into seperate integers. I was thinking about trying to do something with a while loop and string stream, but I'm a little stuck. If someone could help me with this, I would really appreciate it.

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

采纳答案by DrM

If the format is like in your example, you could take out the integer like this:

如果格式类似于您的示例,您可以像这样取出整数:

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

where of course in buffer you have the date ("05/11/1996" )

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

回答by LihO

A possible solution might be also based on strptime, however note that this function only validates whether the day is from the interval <1;31>and month from <1;12>, i.e. "30/02/2013"is valid still:

一个可能的解决方案也可能基于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;
}
但由于strptimestrptime并非总是可用并且额外的验证会很好,因此您可以执行以下操作:
  1. extract day, month, year
  2. fill struct tm
  3. normalize it
  4. check whether normalized date is still the same as retrieved day, month, year
  1. 提取日、月、年
  2. struct tm
  3. 正常化
  4. 检查标准化日期是否仍与检索到的日、月、年相同

i.e.:

IE:

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

used as:

用作:

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

which in this case would output date is invalidsince normalization would detect that 29/02/2013has been normalized to 01/03/2013.

在这种情况下将输出,date is invalid因为标准化会检测到29/02/2013已标准化为01/03/2013.

回答by Joe

Another option is to use std::get_timefrom the <iomanip>header (available since C++11). A good example of its use can be found here.

另一种选择是std::get_time从头<iomanip>文件中使用(从 C++11 开始可用)。可以在此处找到其使用的一个很好的示例。

回答by sehe

I'd prefer to use Boost DateTime:

我更喜欢使用 Boost DateTime:

See it Live on Coliru

看到它住在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";
    }
}

Outputs:

输出:

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