我想檢索一個給定檔案的最后修改日期和時間,并將其格式化為一個字串。我希望以一種執行緒和記憶體安全、跨平臺兼容、不需要使用自定義庫的方式來實作這一目標。日期和時間應根據用戶偏好的地區和日期/時間格式進行格式化,正如作業系統所回傳的那樣。
使用C 11/14特性是可以的。使用 Boost 也可以。執行的速度并不特別重要。
以下是我目前的情況:
std::string text;
text = filename;
text = "Savegame: " text lf "_________________" lf;
{
text = "Saved on: "。
const boost::filesystem::path file_name_path{filename};
const boost::filesystem::path save_dir_path{getSaveDir()}。
const boost::filesystem::path& full_file_path = boost::filesystem::absolute(file_name_path, save_dir_path)。
std::time_t last_saved_time{};
std::stringstream last_saved_string_stream{};
last_saved_string_stream.exceptions(std::ios_base::failbit)。
try
{
std::locale users_preferred_locale{std::locale(")};
boost::gregorian::date_facet output_facet{}。
last_saved_string_stream.imbue(users_preferred_locale)。
last_saved_string_stream.imbue(std::locale(last_saved_string_stream.getloc(), &output_facet) )。
output_facet.format("%x")。
last_saved_time = boost::filesystem::last_write_time(full_file_path)。
auto last_saved_time_point{boost::chrono::system_clock::from_time_t(last_saved_time)};
last_saved_string_stream << last_saved_time_point。
}
catch (boost::filesystem::filesystem_error& fse)
{
VS_LOG_AND_FLUSH(fatal, (boost::format("Filesystem Error determining savegame's last saved date/time: %1%") % fse.what())。
}
catch (std::exception& e)
{
VS_LOG_AND_FLUSH(fatal, (boost::format("General Error determining savegame's last saved date/time: %1%") % e.what() )。
}
catch (...)
{
VS_LOG_AND_FLUSH(fatal, "確定savegame的最后保存日期/時間時出現了嚴重的錯誤! 剛剛發生了什么?")。)
}
text = last_saved_string_stream.str() lf;
}
VS_LOG_AND_FLUSH是一個宏(我知道,我知道),它以指定的日志級別和訊息呼叫BOOST_LOG_TRIVIAL,然后重繪 所有的boost日志匯,加上stdout和stderr。
getSaveDir()的實作對于本問題的目的來說并不重要。讓我們接受它回傳一個std::string,其中包含這個程式的保存游戲的目錄。
即使走到這一步也比它應該走的路要難得多。我發現人們實際使用這些庫的例子少得令人吃驚 - 尤其是將它們放在一起使用。
這段代碼可以編譯,但在 Windows 上運行時卻無聲地崩潰了。同時,在 Linux 上,它可以運行,但將檔案的時間戳顯示為 1970 年 1 月 1 日以來的某個巨大的納秒數。這不是正確的格式。例如,在美國,該格式應該是 "9/10/2021 5:30 PM",或者在一些歐洲國家,是 "10/09/2021 17:30:00"。
我做錯了什么?我應該怎么做呢?
我在output_facet.format(...)行中嘗試了幾個不同的格式字串。我已經嘗試了"%c"和"%x",每個都有相同的結果。
uj5u.com熱心網友回復:
你很接近了。一些注意事項:
facets是由它們的locales所擁有的,你不應該在那里采取區域變數的地址:
boost::gregorian::date_facet output_facet{}; last_saved_string_stream.imbue(std::locale(last_saved_string_stream.getloc(), & output_facet) output_facet.format("%x")。應該是
last_saved_string_stream.imbue(std::locale(last_saved_string_stream.getloc)。 new boost::gregorian::date_facet("%x") )。事實上,多余的imbue可能會被合并:
。
std::stringstream last_saved_string_stream{}; last_saved_string_stream.exceptions(std::ios_base::failbit)。 std::locale users_preferred_locale{std::locale(")}; last_saved_string_stream.imbue(users_preferred_locale)。 last_saved_string_stream.imbue(std::locale(last_saved_string_stream.getloc(), new boost::gregorian::date_facet("%x") )。可以變成
std::stringstream ss{}; ss.exceptions(std::ios_base::failbit)。 ss.imbue(std::locale(""), new boost::gregorian::date_facet("%x") )。注意,你可以減少stringstream和time_t變數上多余的范圍
向Chrono time_point的轉換沒有幫助。這些面只適用于Boost DateTime庫型別,而不是Chrono。相反,轉換為
ptime或local_date_time:ss << boost::posix_time::from_time_t( fs::last_write_time(full_file_path))。)為什么要在一個函式中結合字串連接、std iostreams 和 boost::format?
根據我的測驗,你也許可以不使用imbue:
。
#include <boost/filesystem.hpp>/span>
#include <boost/format.hpp>/span>
#include <boost/chrono.hpp>/span>
#include <boost/date_time.hpp>
namespace fs = boost::filesystem;
使用 namespace std::string_literals。
// Mocks
enum severity { fatal };
template <typename Msg>
static void VS_LOG_AND_FLUSH( severity, Msg const& msg) { std: :clog << msg << std::endl; }
fs::path getSaveDir() { return fs::current_path(); }
static constexpr char const* lf ="
"。
std::string DescribeSaveGame(std::string filename, bool do_imbue)
{
std::stringstream ss;
ss.exceptions(std::ios_base::failbit)。
if (do_imbue) {
ss.imbue(std::locale(std::locale)。
new boost::gregorian::date_facet("%x") )。
}
ss << "Savegame: " << filename << lf << "_________________" < < lf;
try {
ss << "Saved on: "。
ss << boost::posix_time::from_time_t(
fs::last_write_time(fs::absolute(filename, getSaveDir()))))
<< lf;
return ss.str()。
} catch (std::exception const& e) {
VS_LOG_AND_FLUSH(fatal,
"General Error determining savegame's last saved date/time: "s
e.what()) 。
} catch (...) {
VS_LOG_AND_FLUSH(fatal,
"非常糟糕的錯誤,決定了savegame的最后一次保存"。
"日期/時間! 剛剛發生了什么?")。)
}
return "error"; // TODO?
}
int main() {
std::cout << DescribeSaveGame("test.bin", true) << std::endl;
std::cout << DescribeSaveGame("test.bin", false) << std::endl;
}
列印
Savegame: test.bin
_________________
保存時間: 2021-Sep-11 14:29:09
保存游戲:test.bin
_________________
保存時間:2021-9-11 14:29:09
最簡單的
上面的內容意味著你可能可以把整個事情簡化為一個詞法_cast:
std::string DescribeSaveGame(std::string const& filename) {
auto save_date = boost::posix_time::from_time_t(
last_write_time(absolute(filename, getSaveDir())));
return "Savegame: " 檔案名 lf "_________________" lf
"保存在。" boost::lexical_cast<std::string>(save_date) lf;
}
仍然可以列印
Savegame: test.bin
_____________
保存于: 2021-Sep-11 14:37:10
uj5u.com熱心網友回復:
首先。事實證明,getSaveDir()畢竟不能可靠地回傳預期值。這個問題現在已經解決了。
第二:我的 catch 子句依賴于更多的格式化字串輸出,在一些字串輸出已經失敗之后。這不一定是個好主意。
第三:我的catch子句并沒有真正退出程式。VS_LOG_AND_FLUSH不包括這個功能。
第四:我在這個.cpp檔案的頂部包含的特定頭檔案集原來是很重要的。甚至順序也可能很重要。以下是我發現的有效方法:
#include <boost/filesystem.hpp>/span>
#include <boost/chrono/time_point.hpp>/span>
#include <boost/chrono/io/time_point_io.hpp>/span>
#include <boost/chrono/chrono.hpp>/span>
#include <iostream>
#include <sstream>
#include <Chrono>
#include <locale>
最后。我所尋找的具體輸出函式是boost::chrono::time_fmt。
以下是我現在的代碼:
string text;
text = filename;
text = "Savegame: " text lf "_________________" lf;
try
{
text = "Saved on: "。
const boost::filesystem::path file_name_path{filename};
const boost::filesystem::path save_dir_path{getSaveDir()};
const boost::filesystem::path full_file_path{boost::filesystem::absolute(file_name_path, save_dir_path)};
std::time_t last_saved_time{boost::filesystem::last_write_time(full_file_path)};
boost::Chrono::system_clock::time_point last_saved_time_point{boost::Chrono::system_clock::from_time_t(last_saved_time)};
std::ostringstream last_saved_string_stream{};
last_saved_string_stream << boost::Chrono::time_fmt(boost::Chrono::timezone::local, "%c"/span>)
<< last_saved_time_point;
text = last_saved_string_stream.str() lf;
}
catch (boost:: filesystem::filesystem_error& fse)
{
VS_LOG_AND_FLUSH(fatal, "boost::filesystem::filesystem_error encountered") 。
VS_LOG_AND_FLUSH(fatal, fse.what())。
VSExit(6)。
}
catch (std::exception& e)
{
VS_LOG_AND_FLUSH(fatal, "std::exception encountered") 。
VS_LOG_AND_FLUSH(fatal, e.what() )。)
VSExit(6)。
}
catch (...)
{
VS_LOG_AND_FLUSH(fatal, "遇到未知例外型別!")。
VSExit(6)。
這似乎在Windows和Linux上都能可靠地作業。(代碼尚未在macOS上測驗,但至少在Mac上確實可以編譯。)
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/314114.html
標籤:
