8

我想做的事

从 C++ 内部将 stdout 和 stderr 重定向到一个或多个文件

为什么我需要它

我正在使用一个外部的、预编译的第三方库,它会产生大量的输出,我想将其重定向到一个日志文件以保持控制台的清洁。

条件

兼容性不是问题,代码只能在 Unix 系统上运行。重定向不仅应该影响 c++ 样式的打印 ( std::cout << "hello world" << std::endl),还应该影响 c 样式的打印 ( printf("hello world\n"))。

到目前为止我尝试过的

我在stackoverflow上浏览了半天,阅读了有类似问题的人的多个答案。在这些答案的帮助下,我已经能够将以下代码放在一起:


#include <stdio.h>
#include <iostream>
#include <fcntl.h>
#include "unistd.h"

const int stdoutfd(dup(fileno(stdout)));

int redirect_stdout(const char* fname){
  fflush(stdout);
  int newstdout = open(fname, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP |     S_IROTH);
  dup2(newstdout, fileno(stdout));
  close(newstdout);
}

int restore_stdout(){
  fflush(stdout);
  dup2(stdoutfd, fileno(stdout));
  close(stdoutfd);
  return stdoutfd;
}

int main(){
  redirect_stdout("/dev/null");
  std::cout << "invisible 1" << std::endl;
  restore_stdout();
  std::cout << "visible 1" << std::endl;
  redirect_stdout("/dev/null");
  std::cout << "invisible 2" << std::endl;
  restore_stdout();
  std::cout << "visible 2" << std::endl;
  return 0;
}

我希望看到的:

visible 1
visible 2

我实际看到的

visible 1

也就是说,第一次使用这种机制时,它可以工作 - 但如果再次使用,恢复输出将不起作用。有人可以向我指出我需要改变什么才能让机制无限频繁地工作吗?

4

6 回答 6

4

如果您希望能够重用它,请不要关闭stdoutfd.restore_stdout

于 2013-08-06T17:03:28.667 回答
4

除了freopen()的afr0ck答案之外,我想说的是,在使用时我们应该小心。一旦一个流喜欢或被重新打开并分配新的目的地(这里是'output.txt'文件),除非它被明确地改变,否则它总是保留给程序。freopen()stdoutstdin

freopen("output.txt", "a", stdout);

在这里,标准输出流stdout被重新打开并分配了“output.txt”文件。之后,无论何时我们使用printf()或任何其他stdout流,例如 -putchar()然后每个输出都将转到'output.txt'。要恢复默认行为(即在控制台/终端中打印输出),printf()或者putchar()我们可以使用以下代码行 -

  • 对于 gcc,Linux 发行版,如 ubuntu -freopen("/dev/tty", "w", stdout);
  • 对于 Mingw C/C++,windows -freopen("CON", "w", stdout);

请参阅下面的代码示例 -

#include <stdio.h>

int main() {
    
    printf("No#1. This line goes to terminal/console\n");

    freopen("output.txt", "a", stdout);
    printf("No#2. This line goes to the \"output.txt\" file\n");
    printf("No#3. This line aslo goes to the \"output.txt\" file\n");

    freopen("/dev/tty", "w", stdout); /*for gcc, diffrent linux distro eg. - ubuntu*/
    //freopen("CON", "w", stdout); /*Mingw C++; Windows*/
    printf("No#4. This line again goes to terminal/console\n");        

}

此代码在您的当前目录中生成一个“output.txt”文件,No#2 和 No#3 将打印在“output.txt”文件中。

谢谢

于 2015-01-02T18:25:05.960 回答
3

您是否正在寻找这样的东西:-

int main()
{
    // Save original std::cin, std::cout
    std::streambuf *coutbuf = std::cout.rdbuf();
    std::streambuf *cinbuf = std::cin.rdbuf(); 

    std::ofstream out("outfile.txt");
    std::ifstream in("infile.txt");

    //Read from infile.txt using std::cin
    std::cin.rdbuf(in.rdbuf());

    //Write to outfile.txt through std::cout 
    std::cout.rdbuf(out.rdbuf());   

    std::string test;
    std::cin >> test;           //from infile.txt
    std::cout << test << "  "; //to outfile.txt

    //Restore back.
    std::cin.rdbuf(cinbuf);   
    std::cout.rdbuf(coutbuf); 

}

从我之前的回答

于 2013-08-06T17:11:25.803 回答
2

好吧,我最好使用 freopen()

使用语法:

freopen("RedToFile","r",stdout);
or
freopen("/dev/null","a",stdout);

“stderr”也是如此

于 2013-10-28T19:30:28.337 回答
1

对于 C++ iostream,您可以使用非常量重载rdbuf to 设置std::coutstd::filebuf. (这最好通过 RAII 类来完成,因为您必须在离开 main 之前恢复它。)对于 C FILE*,您可以使用freopen,但我认为您无法恢复它。

FWIW:这两个解决方案都只使用标准的 C++ 或 C,所以应该是可移植的。

于 2013-08-06T17:26:05.543 回答
1

我受到@POW 和@James Kanze 的回答的启发,并整理了一个用于重定向std::cout到文件的小 RAII 类。它旨在演示该原理。

代码:

#include <iostream>
#include <fstream>
#include <string>

// RAII for redirection
class Redirect {
    public:
    
    explicit Redirect(const std::string& filenm):
        _coutbuf{ std::cout.rdbuf() },   // save original rdbuf
        _outf{ filenm }
    {
        // replace cout's rdbuf with the file's rdbuf
        std::cout.rdbuf(_outf.rdbuf());
    }
    
    ~Redirect() {
        // restore cout's rdbuf to the original
        std::cout << std::flush;
        _outf.close();    ///< not really necessary
        std::cout.rdbuf(_coutbuf); 
    }
    
    private:
    
    std::streambuf* _coutbuf;
    std::ofstream _outf;
};

// == MAIN ==

int main(int argc, char* argv[]) {
    std::cout << "This message is printed to the screen" << std::endl;
    {
        // scope for the redirection
        Redirect redirect{ "output.txt" };
        std::cout << "This message goes to the file" << std::endl;
    }
    std::cout << "Printing to the screen again" << std::endl;
}

输出:

此消息打印到屏幕上

再次打印到屏幕

文件内容"output.txt"

此消息转到文件

于 2020-05-25T19:17:03.533 回答