【问题标题】:Redirect data from multiple streams to a single stream while keeping original data将数据从多个流重定向到单个流,同时保留原始数据
【发布时间】:2021-12-24 19:19:57
【问题描述】:

简单地说,我正在尝试创建一个链接到文件std::ofstream outFile{pathToFile, std::ios_base::app}; 的流。该文件是一个日志文件,理想情况下,它将接收 stderr 和 stdout 的副本。例如,当发生错误时,该错误将打印在控制台文件中。

我尝试过使用freopen(pathToFile, "a+", stderr);std::cerr.rdbuf(outFile.rdbuf());,但是,它们都完全重定向了输出,类似于bash 中的管道。我也想在控制台看到错误。

对 C++ 流有点陌生,我不太确定如何实现这一点。我会看到它以另一种语言完成的方式是“订阅”stderr,因此每次stderr 更改和更新时都会通知我的原始流。另一种方法是覆盖stderr,登录到我的文件并调用原始文件,但由于程序员本身通常不调用stderr,我不确定我该怎么做。

需要注意的是,我使用的是windows,所以无法访问unistd.h。我还想避免使用STL 或第三方库。

【问题讨论】:

    标签: c++ iostream


    【解决方案1】:

    IOStreams 在内部使用std::streambuf 来实际写入(或读取)字符。如果您只想重定向所有写入另一个流的字符,您可以重定向相应的流缓冲区。但是,您最终应该恢复原始流缓冲区,因为流在​​销毁时会被刷新,并且流缓冲区的生命周期很重要。例如

     #include <iostream>
     #include <fstream>
    
     struct redirect {
         std::ostream&   d_stream;
         std::streambuf* d_orig;
         redirect(std::ostream& out, std::ostream& to)
             : d_stream(out)
             , d_orig(out.rdbuf(to.rdbuf())) {
         }
         ~redirect() { this->d_stream.rdbuf(this->d_orig); }
    };
    
    int main() {
        std::ofstream log("my-log.txt");
        redirect rcout(std::cout, log);
        redirect rcerr(std::cerr, log);
        std::cout << "hello, ";
        std::cerr << "world!\n";
    }
    

    如果您希望重定向的流出现在原始流中,您可以使用“teestream”,它使用流缓冲区将每个字符复制到两个不同的流缓冲区(我有过去曾在多个地方发布过此类流的多个版本,包括stackoverflow)。

    【讨论】:

    • 您在此处显示的示例并没有真正帮助,因为这几乎是我已经拥有的,但是您链接的答案正是我所需要的。我已经基本解决了我的问题。但是,正如您提到的,我想询问有关恢复缓冲区的问题。到目前为止,我只是将 cout 的缓冲区设置为 teestream 的缓冲区,并且效果很好。但是您提到了恢复缓冲区。我没有对旧 cout 进行任何备份,因为我希望从应用程序启动到退出都发生这种行为。在那种情况下,从不恢复缓冲区仍然可以吗?假设我经常冲水。
    • @EtiennePoulin:我按照要求回答了这个问题,并链接到了可能要问的问题(X/Y 问题的常见情况)。在std::cout(同样std::cerr)被销毁的未指定时间,缓冲区将被强制刷新(即,pubsync() 被调用)。为此,指向 必须 的流缓冲区是有效的。最简单的方法是恢复其原始流缓冲区。如果您愿意泄漏teebuf 并且没有正确销毁它,您可以保持安装状态。这会违反我的个人代码卫生,但它应该可以工作。
    • 抱歉问题中的歧义。那么,据我了解,您的意思是当 std:cout 被破坏时(很可能是在程序停止运行时?)它需要在此之前恢复到原始流?所以,假设我有一个 Logger 类,它处理所有日志记录并在执行开始时被实例化,如果我在所述 Logger 类的析构函数中恢复 cout 和 cerr 流,会好吗?因为否则,我不知道何时恢复它们,因为我希望这个 teestream 只要程序正在运行就可以启动。
    • @EtiennePoulin:是的,当替换流缓冲区被破坏时恢复流缓冲区是一个合理的地方。最终结果可能是,如果在记录器被销毁后写入某些日志,则不会捕获它们。这类似于在构造记录器之前未捕获一些日志。
    • @EtiennePoulin:不客气。问题也不仅仅是理论上的问题:大约 25 年前,这让我意识到创建自定义流的派生 IOStream(例如,oteestream 创建teebuf)应该使流缓冲区(一个包含成员的类其中)第一个[可能是private]virtual基类:如果流缓冲区是派生流的成员,它将在基类构造函数运行之前被销毁并且程序可能会崩溃——这种崩溃确实发生了(并且可能仍然会发生)。
    猜你喜欢
    • 2012-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-21
    • 2013-09-05
    相关资源
    最近更新 更多