redirigir stdout / stderr a una cadena

ha habido muchas preguntas anteriores sobre la redirección de stdout / stderr a un archivo. ¿Hay alguna manera de redirigir stdout / stderr a una cadena?

Sí, puedes redirigirlo a std::stringstream :

 std::stringstream buffer; std::streambuf * old = std::cout.rdbuf(buffer.rdbuf()); std::cout < < "Bla" << std::endl; std::string text = buffer.str(); // text will now contain "Bla\n" 

Puede usar una clase de guardia simple para asegurarse de que el búfer siempre se restablezca:

 struct cout_redirect { cout_redirect( std::streambuf * new_buffer ) : old( std::cout.rdbuf( new_buffer ) ) { } ~cout_redirect( ) { std::cout.rdbuf( old ); } private: std::streambuf * old; }; 

Puedes usar esta clase:

 #include  #include  #include  #include  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; }; 

llame a BeginCapture() cuando necesite iniciar la captura
llame a EndCapture() cuando necesite detener la captura
llame a GetCapture() para recuperar la salida capturada

Para proporcionar una solución multiproceso y segura para subprocesos, he adaptado el enfoque de rmflow en una interfaz similar. Como esta clase modifica los descriptores de archivos globales, la adapté a una clase estática protegida por mutex que protege contra instancias múltiples que destruyen los descriptores de archivos globales. Además, la respuesta de rmflow no limpia todos los descriptores de archivos usados, lo que puede ocasionar problemas para abrir nuevos (para flujos de salida o archivos) si se utilizan muchas llamadas BeginCapture () y EndCapture () en una aplicación. Este código ha sido probado en Windows 7/8, Linux, OSX, Android e iOS.

NOTA: para usar std :: mutex debe comstackr contra c ++ 11. Si no puede / no puede usar c ++ 11, puede eliminar las llamadas mutex por completo (sacrificando la seguridad de la secuencia) o puede encontrar un mecanismo de sincronización heredado para Termina el trabajo.

 #ifdef _MSC_VER #include  #define popen _popen #define pclose _pclose #define stat _stat #define dup _dup #define dup2 _dup2 #define fileno _fileno #define close _close #define pipe _pipe #define read _read #define eof _eof #else #include  #endif #include  #include  #include  class StdCapture { public: static void Init() { // make stdout & stderr streams unbuffered // so that we don't need to flush the streams // before capture and after capture // (fflush can cause a deadlock if the stream is currently being std::lock_guard lock(m_mutex); setvbuf(stdout,NULL,_IONBF,0); setvbuf(stderr,NULL,_IONBF,0); } static void BeginCapture() { std::lock_guard lock(m_mutex); if (m_capturing) return; secure_pipe(m_pipe); m_oldStdOut = secure_dup(STD_OUT_FD); m_oldStdErr = secure_dup(STD_ERR_FD); secure_dup2(m_pipe[WRITE],STD_OUT_FD); secure_dup2(m_pipe[WRITE],STD_ERR_FD); m_capturing = true; #ifndef _MSC_VER secure_close(m_pipe[WRITE]); #endif } static bool IsCapturing() { std::lock_guard lock(m_mutex); return m_capturing; } static bool EndCapture() { std::lock_guard lock(m_mutex); if (!m_capturing) return; m_captured.clear(); secure_dup2(m_oldStdOut, STD_OUT_FD); secure_dup2(m_oldStdErr, STD_ERR_FD); const int bufSize = 1025; char buf[bufSize]; int bytesRead = 0; bool fd_blocked(false); do { bytesRead = 0; fd_blocked = false; #ifdef _MSC_VER if (!eof(m_pipe[READ])) bytesRead = read(m_pipe[READ], buf, bufSize-1); #else bytesRead = read(m_pipe[READ], buf, bufSize-1); #endif if (bytesRead > 0) { buf[bytesRead] = 0; m_captured += buf; } else if (bytesRead < 0) { fd_blocked = (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR); if (fd_blocked) std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } while(fd_blocked || bytesRead == (bufSize-1)); secure_close(m_oldStdOut); secure_close(m_oldStdErr); secure_close(m_pipe[READ]); #ifdef _MSC_VER secure_close(m_pipe[WRITE]); #endif m_capturing = false; } static std::string GetCapture() { std::lock_guard lock(m_mutex); return m_captured; } private: enum PIPES { READ, WRITE }; int StdCapture::secure_dup(int src) { int ret = -1; bool fd_blocked = false; do { ret = dup(src); fd_blocked = (errno == EINTR || errno == EBUSY); if (fd_blocked) std::this_thread::sleep_for(std::chrono::milliseconds(10)); } while (ret < 0); return ret; } void StdCapture::secure_pipe(int * pipes) { int ret = -1; bool fd_blocked = false; do { #ifdef _MSC_VER ret = pipe(pipes, 65536, O_BINARY); #else ret = pipe(pipes) == -1; #endif fd_blocked = (errno == EINTR || errno == EBUSY); if (fd_blocked) std::this_thread::sleep_for(std::chrono::milliseconds(10)); } while (ret < 0); } void StdCapture::secure_dup2(int src, int dest) { int ret = -1; bool fd_blocked = false; do { ret = dup2(src,dest); fd_blocked = (errno == EINTR || errno == EBUSY); if (fd_blocked) std::this_thread::sleep_for(std::chrono::milliseconds(10)); } while (ret < 0); } void StdCapture::secure_close(int & fd) { int ret = -1; bool fd_blocked = false; do { ret = close(fd); fd_blocked = (errno == EINTR); if (fd_blocked) std::this_thread::sleep_for(std::chrono::milliseconds(10)); } while (ret < 0); fd = -1; } static int m_pipe[2]; static int m_oldStdOut; static int m_oldStdErr; static bool m_capturing; static std::mutex m_mutex; static std::string m_captured; }; // actually define vars. int StdCapture::m_pipe[2]; int StdCapture::m_oldStdOut; int StdCapture::m_oldStdErr; bool StdCapture::m_capturing; std::mutex StdCapture::m_mutex; std::string StdCapture::m_captured; 

llame a Init() una vez (antes de la captura) para eliminar el almacenamiento en búfer a stdout / stderr

llame a BeginCapture() cuando necesite iniciar la captura

llame a EndCapture() cuando necesite detener la captura

llame a GetCapture() para recuperar la salida capturada

llame a IsCapturing() para ver si stdout / stderr está actualmente redirigido

Como su pregunta está etiquetada con C y con C ++, parece apropiado mencionar que aunque no puede asociar una cadena a un ARCHIVO * en C estándar, existen varias bibliotecas no estándar que lo permiten. glibc es casi estándar, por lo que puede ser perfectamente feliz usando fmemopen () Vea http://www.gnu.org/s/libc/manual/html_mono/libc.html#String-Streams

he proporcionado una variación qt osx ready del código Björn Pollex

 #include  #include  #include  #include  #include  #include  class CoutRedirect { public: CoutRedirect() { old = std::cout.rdbuf( buffer.rdbuf() ); // redirect cout to buffer stream } std::string getString() { return buffer.str(); // get string } ~CoutRedirect( ) { std::cout.rdbuf( old ); // reverse redirect } private: std::stringstream buffer; std::streambuf * old; };