簡體   English   中英

如何在 Windows 上解析由 std::put_time("%x") 創建的字符串?

[英]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.202009/15/202015. Sept. 2020等。

這是預期的,因為%x被描述為在cppreferencewrites 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()內部使用strftimestd::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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM