我想从字符串中获取日期,月份和年份信息。
示例日期字符串:2020年7月30日,星期四00:51:08 -0700(PDT)
PDT此处是太平洋夏令时间。创建文件时,此字符串偏移量(-0700)可以根据系统时区进行更改。
我需要编写一个c ++程序来从该字符串中提取日期,月份和年份。
关于如何解决此问题的任何想法?
答案 0 :(得分:2)
这是一个进化的故事。正确的答案很大程度上取决于您当前使用的工具集(它有多现代)。即使它完全是现代的,还是会有更好的工具。
在C ++ 98中,我们可以直立。我们有工具可以扫描int
个数组中的char
个。 scanf
是执行此操作的工具。此结果的类型不安全,但是我们可以扫描整数和字符串,然后将这些值重新解释为日期的组成部分:年,月和日。这可能看起来像这样:
#include <cstdio>
#include <cstring>
#include <iostream>
int
main()
{
using namespace std;
string s = "Thu, 30 Jul 2020 00:51:08 -0700 (PDT)";
char const* months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
char wd[4] = {};
int d;
char mon[4] = {};
int y;
sscanf(s.c_str(), "%s %d %s %d", wd, &d, mon, &y);
int m;
for (m = 0; m < 12; ++m)
if (strcmp(months[m], mon) == 0)
break;
++m;
cout << y << '\n';
cout << m << '\n';
cout << d << '\n';
}
这将输出:
2020
7
30
注释:
" 00:51:08 -0700 (PDT)"
从未被解析。 可以进行解析。但这还需要做很多工作。int
,如果将它们混合在一起,那是运行时错误,而不是编译时错误。使用C ++ 98,还有一种流行的但是 非标准解决方案:strptime
。
#include <time.h>
#include <iostream>
int
main()
{
using namespace std;
string s = "Thu, 30 Jul 2020 00:51:08 -0700 (PDT)";
tm tm;
strptime(s.c_str(), "%a, %d %b %Y %T", &tm);
cout << tm.tm_year + 1900 << '\n';
cout << tm.tm_mon + 1 << '\n';
cout << tm.tm_mday << '\n';
cout << tm.tm_hour << '\n';
cout << tm.tm_min << '\n';
cout << tm.tm_sec << '\n';
}
strptime
在POSIX标准中,但不在C或C ++标准中。 MS Visual Studio也支持它。因此,这是一个受欢迎的扩展。并且有充分的理由。它的级别更高,并将结果放入struct tm
中:一种表示日期/时间的类型;类型安全性的开端。
输出:
2020
7
30
0
51
8
仍然存在一些问题:
" -0700 (PDT)"
不会被解析。无法要求strptime
执行此操作。tm
的不同字段上存在偏移量和不一致的偏移量。例如,月份从零开始,日期从一开始。但是至少它也知道如何解析时间,而且相对容易。strptime
返回NULL
。随着C ++ 11到达strptime
周围的实际C ++包装器,并由std::get_time
正式由C ++标准认可:
#include <iomanip>
#include <iostream>
#include <sstream>
int
main()
{
using namespace std;
string s = "Thu, 30 Jul 2020 00:51:08 -0700 (PDT)";
istringstream in{s};
in.exceptions(ios::failbit);
tm tm;
in >> get_time(&tm, "%a, %d %b %Y %T");
cout << tm.tm_year + 1900 << '\n';
cout << tm.tm_mon + 1 << '\n';
cout << tm.tm_mday << '\n';
cout << tm.tm_hour << '\n';
cout << tm.tm_min << '\n';
cout << tm.tm_sec << '\n';
}
使用C ++包装器,您可以从流中进行解析,这使您可以在解析失败时引发异常。但是它仍然是一个简单的包装器,因此结果只是一个tm
。这与以前的解决方案具有相同的怪异性。
输出与以前的解决方案相同:
2020
7
30
0
51
8
尽管C ++ 11中引入了强类型std::chrono
time_point
/ duration
系统,但直到C ++ 20才将其与民用日历集成在一起,类似于get_time
的功能,并且远远超出了此范围。
#include <chrono>
#include <iostream>
#include <sstream>
int
main()
{
using namespace std;
using namespace std::chrono;
string s = "Thu, 30 Jul 2020 00:51:08 -0700 (PDT)";
istringstream in{s};
in.exceptions(ios::failbit);
local_seconds t;
in >> parse("%a, %d %b %Y %T %z (%Z)", t);
auto td = floor<days>(t);
year_month_day ymd{td};
hh_mm_ss hms{t-td};
cout << ymd << ' ' << hms << '\n';
cout << ymd.year() << '\n';
cout << ymd.month() << '\n';
cout << ymd.day() << '\n';
cout << hms.hours() << '\n';
cout << hms.minutes() << '\n';
cout << hms.seconds() << '\n';
}
输出:
2020-07-30 00:51:08
2020
Jul
30
0h
51min
8s
首先要注意的是 much 更强的类型安全性。不再需要将所有内容都转换为int
来打印出来。而且不再需要转换为int
来执行其他操作,例如算术和比较。
例如ymd.year()
的类型为std::chrono::year
,而不是int
。如有必要,可以在这两种表示形式之间进行显式转换。但这通常是不必要的,类似于危险的reinterpret_cast
。
不再存在诸如1900之类的不直观的偏见,或者在意外的地方不再采用从零开始的计数。
输出通常包括便于调试的单位。
这里解析了" -0700 (PDT)"
!这些值未在结果中使用,但必须在其中,否则会出现解析错误。而且,如果您想获取这些值,可以通过非常简单的更改获得它们:
string abbrev;
minutes offset;
in >> parse("%a, %d %b %Y %T %z (%Z)", t, abbrev, offset);
...
cout << offset << '\n';
cout << abbrev << '\n';
现在输出包括:
-420min
PDT
如果需要使用UTC(而不是当地时间)的字段,那是一个简单的更改:
sys_seconds t;
代替:
local_seconds t;
现在,从解析的时间点减去偏移量,以得出UTC time_point(基于std::chrono::time_point
的{{1}}),并且输出更改为:
system_clock
这使您可以轻松地将本地时间和偏移量解析为2020-07-30 07:51:08
2020
Jul
30
7h
51min
8s
。
尽管还没有发货(如我所写),但供应商 正在努力实现这一目标。同时,您可以使用free, open-source, header-only C++20 <chrono>
preview library来获得此功能,该功能可与C ++ 11/14/17一起使用。只需添加system_clock::time_point
和#include "date/date.h"
,一切正常。尽管使用C ++ 11/14,您将需要用using namespace date;
代替hh_mm_ss<seconds> hms{t-td};
(缺少CTAD)。
答案 1 :(得分:0)
#include <time.h>
char *strptime(const char *buf, const char *format, struct tm *tm);