I have a library that outputs stuff to t开发者_如何学Che console.
How do I change stdout such that it writes to a file instead of to the console?
Try the following:
// Let fs be your file stream …
cout.rdbuf(fs.rdbuf());
This re-sets the internal stream buffer.
You can just close(1)
and then dup2
any file descriptor on stdout(1).
Taking a step back, it looks like you may have a library design problem.
In many libraries, you should be cautious about writing anywhere that your user has not told you to write. That means you should not be writing to standard output at all, and certainly not redirecting it. Don't forget, other parts of the program may depend on standard output still working as standard output, not writing to the file the your library wants to us. If it is necessary to write somewhere, either the user should tell you where to write, or you should simply open a file and have the library write to that file, and not to standard output. The library may be OK writing to standard error if there is an error, but you should be cautious about even that.
Clearly, there are exceptions to the sweeping statements here (one only has to point to the standard I/O library). But the fact that you are discussing redirecting standard output from within a library for the convenience of the library means the design is suspect.
You can use this class:
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string>
class StdCapture
{
public:
StdCapture(): m_capturing(false), m_init(false), m_oldStdOut(0), m_oldStdErr(0)
{
m_pipe[READ] = 0;
m_pipe[WRITE] = 0;
if (_pipe(m_pipe, 65536, O_BINARY) == -1)
return;
m_oldStdOut = dup(fileno(stdout));
m_oldStdErr = dup(fileno(stderr));
if (m_oldStdOut == -1 || m_oldStdErr == -1)
return;
m_init = true;
}
~StdCapture()
{
if (m_capturing)
{
EndCapture();
}
if (m_oldStdOut > 0)
close(m_oldStdOut);
if (m_oldStdErr > 0)
close(m_oldStdErr);
if (m_pipe[READ] > 0)
close(m_pipe[READ]);
if (m_pipe[WRITE] > 0)
close(m_pipe[WRITE]);
}
void BeginCapture()
{
if (!m_init)
return;
if (m_capturing)
EndCapture();
fflush(stdout);
fflush(stderr);
dup2(m_pipe[WRITE], fileno(stdout));
dup2(m_pipe[WRITE], fileno(stderr));
m_capturing = true;
}
bool EndCapture()
{
if (!m_init)
return false;
if (!m_capturing)
return false;
fflush(stdout);
fflush(stderr);
dup2(m_oldStdOut, fileno(stdout));
dup2(m_oldStdErr, fileno(stderr));
m_captured.clear();
std::string buf;
const int bufSize = 1024;
buf.resize(bufSize);
int bytesRead = 0;
if (!eof(m_pipe[READ]))
{
bytesRead = read(m_pipe[READ], &(*buf.begin()), bufSize);
}
while(bytesRead == bufSize)
{
m_captured += buf;
bytesRead = 0;
if (!eof(m_pipe[READ]))
{
bytesRead = read(m_pipe[READ], &(*buf.begin()), bufSize);
}
}
if (bytesRead > 0)
{
buf.resize(bytesRead);
m_captured += buf;
}
return true;
}
std::string GetCapture() const
{
std::string::size_type idx = m_captured.find_last_not_of("\r\n");
if (idx == std::string::npos)
{
return m_captured;
}
else
{
return m_captured.substr(0, idx+1);
}
}
private:
enum PIPES { READ, WRITE };
int m_pipe[2];
int m_oldStdOut;
int m_oldStdErr;
bool m_capturing;
bool m_init;
std::string m_captured;
};
call BeginCapture()
when you need to start capture
call EndCapture()
when you need to stop capture
call GetCapture()
to retrieve captured output
精彩评论