[英]How to parse a string created by std::put_time("%x") on Windows?
讓我們假設 Windows 上的第三方組件使用以下函數創建一個表示日期的字符串:
std::string getDate() {
std::tm t = {};
std::time_t now = std::time(nullptr);
localtime_s(&t, &now);
std::stringstream s;
s.imbue(std::locale(""));
s << std::put_time(&t, "%x");
return s.str();
}
根據您的系統區域設置和設置的短日期格式你得到的字符串就像15.09.2020
或09/15/2020
或15. Sept. 2020
等。
這是預期的,因為%x
被描述為在cppreference上writes localized date representation (locale dependent)
。
如何將std::put_time("%x")
生成的字符串解析回std::tm
(假設語言環境和短日期格式系統設置相同)?
STL
std::tm parseDate1(const std::string& date) {
std::tm t = {};
std::istringstream ss(date);
ss.exceptions(std::ifstream::failbit);
ss.imbue(std::locale(""));
ss >> std::get_time(&t, "%x");
return t;
}
不起作用,因為 std::get_time 的實現需要硬編碼格式"%d / %m / %y
for %x
in xlocime
。
促進
std::tm parseDate2(const std::string& date) {
boost::gregorian::date d;
auto* input_facet = new boost::gregorian::date_input_facet();
input_facet->format("%x");
std::istringstream ss(date);
ss.exceptions(std::ifstream::failbit);
ss.imbue(std::locale(std::locale(""), input_facet));
ss >> d;
return boost::gregorian::to_tm(d);
}
Boost 總是返回1400-Jan-01
因為%x
似乎根本沒有實現解析。
時間
windows上好像沒有。 這里存在一個實現,但編譯和集成似乎並不簡單。
到目前為止我想出的最佳解決方法是使用 Win32 函數EnumDateFormats()
讀取系統DATE_SHORTDATE
格式並將此格式轉換為std::get_time()
語法,因為它不兼容(例如dd.MM.yyyy
需要是對於std::get_time()
轉換為%d.%m.%Y
)。 但這似乎很容易出錯,而不是“正確”的方法......
似乎std::put_time()
內部使用strftime
而std::get_time()
是“自我實現的”。 我原以為std::put_time()
生成的所有內容都應該可以被std::get_time()
使用相同的格式字符串解析。 但這似乎並非如此,而且似乎也沒有記錄在案。 或者我錯過了什么?
Plauger 有一個著名的帖子,他不會解析“Michaelmas 之后的第 14 天”,標准委員會也不能讓他這樣做。 您可以將put_time
用於已知日期,例如 71/2/1,並嘗試剖析結果以重新創建您可以稍后用於解析的詳細模式。
您可以准備所有可能格式的值,例如:
const char* formats[] = {
"%Y.%M.%d",
"%Y/%M/%d",
"%Y %b %d",
...
};
然后,您可以嘗試使用get_time 依次使用格式解析第一個日期,直到解析成功。
之后,您可以保存格式索引並將其用於其他日期解析。
但基本上你在這里遺漏了一些信息。 例如,只需查看:
2020/02/03
如果您不知道生成日期的語言環境或格式,您就無法判斷是 3 月 2 日還是 2 月 3 日。
為了克服這個問題,您可以嘗試對從集合中隨機選擇的多個日期(或所有日期)使用上述格式,讓每個解析成功,這樣您就可以更確定所選擇的格式是正確的。
但這是一個蠻力解決方案。
對於最簡單/最正確的解決方案,有必要對 put_time 產生的日期進行一些限制 - 您必須了解它的格式。
這是迄今為止我想出的最好的:
#include <iostream>
#include <sstream>
#include <locale>
#include <iomanip>
#include <windows.h>
#include <boost/algorithm/string.hpp>
static std::string g_shortDateFormat;
BOOL CALLBACK EnumDateFormatsProc(_In_ LPSTR formatString) {
if (g_shortDateFormat.empty())
g_shortDateFormat = formatString;
return TRUE;
}
std::string getShortDatePattern() {
if (g_shortDateFormat.empty()) {
EnumDateFormatsA(EnumDateFormatsProc, LOCALE_USER_DEFAULT, DATE_SHORTDATE);
boost::algorithm::replace_all(g_shortDateFormat, "yyyy", "%Y");
boost::algorithm::replace_all(g_shortDateFormat, "yy", "%y");
boost::algorithm::replace_all(g_shortDateFormat, "MMMM", "%b");
boost::algorithm::replace_all(g_shortDateFormat, "MMM", "%b");
boost::algorithm::replace_all(g_shortDateFormat, "MM", "%m");
boost::algorithm::replace_all(g_shortDateFormat, "M", "%m");
boost::algorithm::replace_all(g_shortDateFormat, "dddd", "%a");
boost::algorithm::replace_all(g_shortDateFormat, "ddd", "%a");
boost::algorithm::replace_all(g_shortDateFormat, "dd", "d"); // intended to avoid %%d
boost::algorithm::replace_all(g_shortDateFormat, "d", "%d");
}
return g_shortDateFormat;
}
std::string getLocalDate(const std::tm& t) {
std::stringstream s;
s.imbue(std::locale(""));
s << std::put_time(&t, "%x");
return s.str();
}
std::tm parseLocalDate(const std::string& localDate) {
auto format = getShortDatePattern();
std::istringstream is(localDate);
is.imbue(std::locale(""));
is.exceptions(std::istream::failbit);
std::tm t = {};
is >> std::get_time(&t, format.c_str());
return t;
}
std::tm now() {
auto now = std::time(nullptr);
std::tm t = {};
localtime_s(&t, &now);
return t;
}
int main() {
auto t = now();
auto localDate = getLocalDate(t);
auto parsedDate = parseLocalDate(localDate);
std::cout << localDate << " - " << getLocalDate(parsedDate) << std::endl;
return 0;
}
即使我在 Windows 區域設置中輸入了相當奇怪的自定義短日期格式,如DD.MM.YYYY, DDDD
,這也會起作用DD.MM.YYYY, DDDD
會生成類似17.09.2020, Thursday
日期。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.