在Unix C++中将标准输出/错误重定向到文件 - 再次

9

我想要做什么

在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

也就是说,第一次使用此机制时,它可以正常工作 - 但如果再次使用,则无法恢复输出。有人能指出我需要更改什么以使机制无限次地工作吗?
编辑:为什么它与其他人所做的不同?
许多人有类似的问题(因此有“再次”,例如here)。然而,在这里,我特别针对适用于C风格和C++风格打印的解决方案,并且特别关注函数的可重用性 - 这通常在其他问题和答案中没有得到解决。
6个回答

5
除了afr0ckfreopen()答案外,我想说在使用freopen()时我们应该小心。一旦一个流像stdoutstdin被重新打开并分配新的目标(这里是'output.txt'文件),它将一直保留在程序中,除非它已经被明确更改。
freopen("output.txt", "a", stdout);

在这里,标准输出流 stdout 被重新打开并分配给文件 'output.txt'。之后,每当我们使用 printf() 或任何其他像 - putchar() 这样的 stdout 流时,所有输出都将进入 'output.txt'。要恢复默认行为(即在控制台/终端中打印输出)的 printf()putchar(),我们可以使用以下代码行 -
  • 对于gcc、ubuntu等Linux发行版 - 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'的文件,并将第二和第三项打印到'output.txt'文件中。
谢谢。

1
这对我很有用 - 现在我可以获取我正在使用的第三方库的输出。 OP的代码给了我seg错误。请注意,您可以执行freopen(“output.txt”,“w”,stdout); freopen(“output.txt”,“w”,stderr);并将标准输出和标准错误都放入同一个文件中。 - darda

4

如果您希望能够重复使用它,请不要在restore_stdout中关闭stdoutfd


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); 

}

根据我早先的回答


2
感谢您的回答 - 但是,我应该说明我想要重定向不仅是C++风格的打印语句,还包括使用printf的普通C打印语句 - 据我所知,您的解决方案并没有实现这一点。对于不够精确,我编辑了问题。 - carsten

2

我建议您最好使用 freopen()。

使用语法:

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

同样适用于"stderr"。

1
对于C++ iostreams,您可以使用非const重载的rdbuf来将std::cout设置为std::filebuf。(最好通过RAII类完成此操作,因为您必须在离开main之前恢复它。)对于C FILE*,您可以使用freopen,但我不认为您能够恢复它。
顺便说一句:这两个解决方案都仅使用标准C++或C,因此应该是可移植的。

1
我受到了@POW和@James Kanze答案的启发,编写了一个小的RAII类来将std::cout重定向到文件。它旨在演示原理。
代码:
#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"的内容:

这条消息被写入文件中


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接