我正在為一些文本處理撰寫一個命令列實用程式。我需要一個(或兩個)輔助函式來執行以下操作:
- 如果檔案名是
-,則回傳標準輸入/輸出; - 否則,創建并打開一個檔案,檢查錯誤并回傳它。
我的問題來了:設計/實作這樣一個功能的最佳實踐是什么?它應該是什么樣子?
我首先考慮的是老派FILE*:
FILE *open_for_read(const char *filename)
{
if (strcmp(filename, "-") == 0)
{
return stdin;
}
else
{
auto fp = fopen(filename, "r");
if (fp == NULL)
{
throw runtime_error(filename);
}
return fp;
}
}
它可以作業,并且以后是安全的fclose(stdin)(以防萬一沒有忘記),但是我將無法訪問流方法,例如std::getline.
所以我認為,現代 C 方法是將智能指標與流一起使用。起初,我嘗試
unique_ptr<istream> open_for_read(const string& filename);
這適用于ifstream但不適用于cin,因為您無法洗掉cin。所以我必須為這個案例提供一個自定義洗掉器(什么都不做)cin。但突然之間,它無法編譯,因為顯然,當提供自定義洗掉器時,它unique_ptr變成了不同的型別。
最終,在 StackOverflow 上進行了多次調整和搜索后,這是我能想到的最好的:
unique_ptr<istream, void (*)(istream *)> open_for_read(const string &filename)
{
if (filename == "-")
{
return {static_cast<istream *>(&cin), [](istream *) {}};
}
else
{
unique_ptr<istream, void (*)(istream *)> pifs{new ifstream(filename), [](istream *is)
{
delete static_cast<ifstream *>(is);
}};
if (!pifs->good())
{
throw runtime_error(filename);
}
return pifs;
}
}
它是型別安全和記憶體安全的(或者至少我相信是這樣;如果我錯了,請糾正我),但這看起來有點丑陋和樣板,最重要的是,讓它變得如此令人頭疼編譯。
我做錯了什么并且在這里遺漏了什么嗎?一定有更好的辦法。
uj5u.com熱心網友回復:
我可能會把它變成
std::istream& open_for_read(std::ifstream& ifs, const std::string& filename) {
return filename == "-" ? std::cin : (ifs.open(filename), ifs);
}
然后ifstream向函式提供一個。
std::ifstream ifs;
auto& is = open_for_read(ifs, the_filename);
// now use `is` everywhere:
if(!is) { /* error */ }
while(std::getline(is, line)) {
// ...
}
ifs如果它被打開,它將像往常一樣在超出范圍時關閉。
投擲版本可能如下所示:
std::istream& open_for_read(std::ifstream& ifs, const std::string& filename) {
if(filename == "-") return std::cin;
ifs.open(filename);
if(!ifs) throw std::runtime_error(filename ": " std::strerror(errno));
return ifs;
}
uj5u.com熱心網友回復:
作為 Ted 答案的替代方案(我認為我實際上更喜歡它),您可以讓您的自定義洗掉器更聰明一點:
auto stream_deleter = [] (std::istream *stream) { if (stream != &std::cin) delete stream; };
using stream_ptr = std::unique_ptr <std::istream, decltype (stream_deleter)>;
stream_ptr open_for_read (const std::string& filename)
{
if (filename == "-")
return stream_ptr (&std::cin, stream_deleter);
auto sp = stream_ptr (new std::ifstream (filename), stream_deleter);
if (!sp->good ())
throw std::runtime_error (filename);
return sp;
}
然后相同的洗掉器適用于兩種情況,并且沒有打字問題。
現場演示
uj5u.com熱心網友回復:
我過去使用的方法是呼叫 rdbuf 來更改std::cin. 如果您不想使用std::cin. 您必須注意在緩沖區被破壞后不要使用它,但是,這不是 RAII 包裝器無法解決的問題。類似的東西(未經測驗,甚至沒有被證明是正確的):
struct stream_redirector {
stream_redirector(std::iostream& s, std::string const& filename,
std::ios_base::openmode mode = ios_base::in)
: redirected_stream_{s}
{
if (filename != "-") {
stream_.open(filename, mode);
if (stream_) {
throw std::runtime_error(filename ": " std::strerror(errno));
saved_buf_ = redirected_stream_.rdbuf();
redirected_stream_.rdbuf(stream_.rdbuf());
}
}
~stream_redirector() {
if (saved_buf_ != nullptr) {
redirected_stream_.rdbuf(saved_buf_);
}
}
private:
std::stream& redirected_stream_;
std::streambuf* saved_buf_{nullptr};
std::fstream stream_;
};
要使用的:
...
stream_redirector cin_redirector(std::cin, filename);
std::string str;
std::cin >> str;
...
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/436691.html
上一篇:shared_ptr不會增加參考計數,而是指向同一個地址
下一篇:C 懸空指標問題
