redirige stdout / stderr vers une chaîne

Il y a eu beaucoup de questions sur la redirection de stdout / stderr vers un fichier. existe-t-il un moyen de redirect stdout / stderr vers une chaîne?

Oui, vous pouvez le redirect vers un std::ssortingngstream :

 std::ssortingngstream 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" 

Vous pouvez utiliser une classe de garde simple pour vous assurer que le tampon est toujours réinitialisé:

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

Vous pouvez utiliser cette classe:

 #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::ssortingng 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::ssortingng GetCapture() const { std::ssortingng::size_type idx = m_captured.find_last_not_of("\r\n"); if (idx == std::ssortingng::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::ssortingng m_captured; }; 

appelez BeginCapture() lorsque vous devez démarrer la capture
appelez EndCapture() lorsque vous devez arrêter la capture
appeler GetCapture() pour récupérer la sortie capturée

Afin de fournir une solution multi-plateformes sécurisée pour les threads, j’ai adapté l’approche de rmflow à une interface similaire. Comme cette classe modifie les descripteurs de fichiers globaux, je les ai adaptés à une classe statique protégée contre les mutex, qui protège contre les occurrences multiples des descripteurs de fichiers globaux. De plus, la réponse de rmflow ne nettoie pas tous les descripteurs de fichiers utilisés, ce qui peut entraîner des problèmes lors de l’ouverture de nouveaux fichiers (stream de sortie ou fichiers) si de nombreux appels BeginCapture () et EndCapture () sont utilisés dans une application. Ce code a été testé sous Windows 7/8, Linux, OSX, Android et iOS.

NOTE: Pour utiliser std :: mutex, vous devez comstackr avec c ++ 11. Si vous ne pouvez / ne pouvez pas utiliser c ++ 11, vous pouvez supprimer complètement les appels mutex (en sacrifiant la sécurité des threads) ou trouver un mécanisme de synchronisation existant finis le travail.

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

appelez Init() une fois (avant la capture) pour supprimer la mise en tampon sur stdout / stderr

appelez BeginCapture() lorsque vous devez démarrer la capture

appelez EndCapture() lorsque vous devez arrêter la capture

appeler GetCapture() pour récupérer la sortie capturée

appeler IsCapturing() pour voir si stdout / stderr est actuellement redirigé

Puisque votre question est balisée en C et en C ++, il semble approprié de mentionner que bien que vous ne puissiez pas associer une chaîne à un FILE * dans la norme C, plusieurs bibliothèques non standard le permettent. La glibc est presque standard, vous pouvez donc être parfaitement content avec fmemopen () Voir http://www.gnu.org/s/libc/manual/html_mono/libc.html#Ssortingng-Streams

J’ai fourni une variante de qt osx du code 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::ssortingng getSsortingng() { return buffer.str(); // get ssortingng } ~CoutRedirect( ) { std::cout.rdbuf( old ); // reverse redirect } private: std::ssortingngstream buffer; std::streambuf * old; };