【问题标题】:Is there a good idiom to deal with alternative output streams?是否有处理替代输出流的好习惯?
【发布时间】:2016-07-19 09:46:10
【问题描述】:

我想编写一个简单的程序,根据传递给它的选项,可执行文件会将输出打印到屏幕或文件中。程序很简单。

#include<iostream>
int main(int argc, char* argv[]){
    ... process options...

    std::ostream& out = ... // maybe std::cout, maybe a *new* std::ofstream;
    out << "content\n";
}

是否有一个好的习惯用法可以在运行时引用 std::cout 或文件流?

我尝试过使用指针,但它太可怕了。我无法避免使用指针(更不用说以后需要更丑陋的代码来删除指针)。

#include<iostream>
#include<ofstream>
int main(int argc, char* argv[]){

    std::string file = argc>1?argv[1]:"";
    std::clog << "file: " << file << '\n';
    // if there is no argument it will print to screen
    std::ostream* out = (file=="")?&std::cout:(new std::ofstream(file)); // horrible code
    *out << "content" << std::endl;
    if(out != &std::cout) delete out;

}

我不知道,也许 C++ 流的某些特性允许这样做。也许我必须使用某种类型的擦除。我认为,问题在于std::cout已经 存在的东西(是全球性的),但std::ofstream 是必须创建的东西。

我设法使用open 并避免使用指针,但它仍然很难看:

int main(int argc, char* argv[]){

    std::string file = argc>1?argv[1]:"";
    std::clog << "file: " << file << '\n';

    std::ofstream ofs; 
    if(file != "") ofs.open(file); 
    std::ostream& out = (file=="")?std::cout:ofs;
    out << "content" << std::endl;
}

【问题讨论】:

标签: c++ iostream ostream


【解决方案1】:

我的偏好是使用安装了合适的流缓冲区的流。这是一种直接输出到文件或std::cout的方法:

#include <iostream>
#include <fstream>
int main(int ac, char* av) {
    std::ofstream ofs;
    if (1 < ac) {
       ofs.open(av[1]);
       // handle errors opening the file here
    }
    std::ostream os(file? file.rdbuf(): std::cout.rdbuf());
    // use os ...
}

【讨论】:

  • 这显然是最好的答案,但我是唯一一个投票的人!
  • 这很整洁。很容易忘记流的缓冲区方面。
【解决方案2】:

太多过度设计了。

#include <iostream>
#include <fstream>

int main(int argc, char* argv[]) {

    std::ofstream ofs(argc > 1 ? argv[1] : "");
    std::ostream& os = ofs.is_open() ? ofs : std::cout;

    // use os ...
}

【讨论】:

  • 不错。但可怕。 ofstream 会尝试打开"" 还是无法自动打开? .此外,如果ofs 流由于其他原因(不可写文件)失败,它会做一些意想不到的事情。 (不要误会,我喜欢+1的解决方案)
  • @alfC,阅读std::basic_ofstream 的文档,了解如何准确识别您提到的问题。你真的过度设计了这个。
  • @alfC,“打开""”是什么意思?如何打开没有名称的文件?您是否曾经设法创建一个文件名为空的文件?如果您尝试打开不可写文件进行写入,您认为会发生什么?提示:它不会做任何意想不到的事情。学会使用你的工具,而不是编写大量脆弱的代码来处理非问题。
  • 1) 如果没有文件系统或文件系统不可写,我想知道std::ofstream ofs(""); 是否仍会尝试 做某事。 2) 如果 `argv[1] == "/readonlylocation",程序将打印到屏幕而不是仅仅因为它无法以写入模式打开文件而给出错误(这是令人惊讶的结果)。
  • 1) 当然不是! 2)啊,好吧,对用户来说是意外的,是的,这是真的。 Dietmar 的出色回答让您可以轻松检查。
【解决方案3】:

所需流的运行时绑定几乎需要看起来像您已经拥有的那样。

在指针问题上,您可以稍微清理一下...也许像这样?这是假设您只想在参数存在时创建 ofstream

int main(int argc, char* argv[]){ 
    std::string file = argc > 1 ? argv[1] : "";
    std::clog << "file: " << file << '\n';

    // if there is no argument it will print to screen
    std::unique_ptr<std::ostream> fp;
    if (file == "")
        fp = std::make_unique<std::ofstream>(file);

    std::ostream& out = (fp && fp->is_open()) ? std::cout : *fp; // not so horrible code

    out << "content" << std::endl;
}

如果不需要动态对象,最简单的可能是列出这个;

int main(int argc, char* argv[]){
    std::string filename = (argc > 1) ? argv[1] : "";

    std::ofstream file(filename);

    // if there is no argument (file) it will print to screen
    std::ostream& out = file.is_open() ? file : std::cout;

    out << "content" << std::endl;
}

【讨论】:

  • 您根本不需要动态分配。你试图通过std::unique_ptrs 自动存储来规避它,但这只是多余的。
  • 我知道,OP 在原始帖子中有它,也许他出于某种原因需要它......他的更新似乎说也许不是......
【解决方案4】:

我经常将这样的东西用于命令行工具:

int main(int, char* argv[])
{
    std::string filename;

    // args processing ... set filename from command line if present
    if(argv[1])
        filename = argv[1];

    std::ofstream ofs;

    // if a filename was given try to open
    if(!filename.empty())
        ofs.open(filename);

    // bad ofs means tried to open but failed
    if(!ofs)
    {
        std::cerr << "Error opeing file: " << filename << '\n';
        return EXIT_FAILURE;
    }

    // Here either ofs is open or a filename was not provided (use std::cout)

    std::ostream& os = ofs.is_open() ? ofs : std::cout;

    // write to output
    os << "Some stuff" << '\n';

    return EXIT_SUCCESS;
}

【讨论】:

    【解决方案5】:

    您可以使用指向流的共享指针来实现多态行为:

    #include <memory>
    #include <fstream>
    #include <sstream>
    #include <iostream>
    
    void nodelete(void*) {}
    
    std::shared_ptr<std::ostream> out_screen_stream() { return std::shared_ptr<std::ostream>(&std::cout, nodelete); }
    std::shared_ptr<std::ostream> out_file_stream() { return std::make_shared<std::ofstream>(); }
    std::shared_ptr<std::ostream> out_string_stream() { return std::make_shared<std::ostringstream>(); }
    
    int main ()
    {
        std::shared_ptr<std::ostream> out;
    
        // case condition:
        out = out_screen_stream();
        out = out_file_stream();
        out = out_string_stream();
    
        *out << "content" << std::endl;
        return 0;
    }
    

    注意:std::shared_ptr 允许管理不同的可能流,其中一些流不应被删除(例如:std::cout)。

    类似,但使用 std::unique_ptr:

    #include <memory>
    #include <fstream>
    #include <sstream>
    #include <iostream>
    
    class Deleter
    {
        public:
        Deleter(bool use_delete = true) : use_delete(use_delete) {}
    
        template <typename T>
        void operator () (const T* p) {
            if(use_delete)
                delete p;
        }
    
        bool nodelete() const { return ! use_delete; }
    
        private:
        bool use_delete;
    };
    
    using unique_ostream_ptr = std::unique_ptr<std::ostream, Deleter>;
    unique_ostream_ptr out_screen_stream() { return unique_ostream_ptr(&std::cout, false); }
    unique_ostream_ptr out_file_stream() { return unique_ostream_ptr{ new std::ofstream }; }
    unique_ostream_ptr out_string_stream() { return unique_ostream_ptr{ new std::ostringstream  }; }
    
    int main ()
    {
        unique_ostream_ptr out;
    
        // case condition:
        out = out_screen_stream();
        out = out_file_stream();
        out = out_string_stream();
    
        *out << "content" << std::endl;
        return 0;
    }
    

    【讨论】:

      【解决方案6】:

      也许是参考?

      #include<iostream>
      #include<ofstream>
      
      
      int main(int argc, char* argv[])
      {
          auto &out = std::cout;
          std::ofstream outFile;
      
          std::string fileName = argc>1?argv[1]:"";
      
          std::clog << "file: " << file << '\n';
          // if there is no argument it will print to screen
          if(!fileName.empty())
          {
              outFile.open(fileName);
              out = outFile;
          }
      
          out<<"one, one, two";
      
          return 0;
      }
      

      【讨论】:

      • 引用不能重新分配,流也不能。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-18
      • 2015-03-28
      • 1970-01-01
      • 1970-01-01
      • 2023-03-29
      • 1970-01-01
      相关资源
      最近更新 更多