【问题标题】:Swapping a stringstream for cout为 cout 交换字符串流
【发布时间】:2026-01-12 13:55:01
【问题描述】:

使用 glibc 的 stdio,我可以将 memstream 交换为 stdout,从而捕获一段编译后输出到 stdout 的代码的输出:

#include <stdio.h>

void swapfiles(FILE* f0, FILE* f1){ FILE tmp; tmp = *f0; *f0 = *f1; *f1 = tmp; }

void hw_c(){ puts("hello c world"); }

int c_capt(){
  FILE* my_memstream;
  char* buf  = NULL;
  size_t bufsiz = 0;

  if( (my_memstream = open_memstream(&buf, &bufsiz)) == NULL) return 1;

  FILE * oldstdout = stdout;

  swapfiles(stdout, my_memstream);
  hw_c();
  swapfiles(stdout, my_memstream);

  fclose(my_memstream);
  printf("Captured: %s\n", buf);
}

我很好奇iostreams 是否也可以这样做。 我天真的尝试不会编译:

#include <iostream>
#include <string>
#include <sstream>

void hw_cc(){ std::cout<<"hello c++ world\n"; }
int cc_capt(){
  using namespace std;

  stringstream ss;
  string capt;

  //std::swap(ss,cout); //<- the compiler doesn't like this
  hw_cc();
  //std::swap(ss,cout); 

  cout<<"Captured: "<<capt<<'\n';
}

int main(int argc, char** argv){
  c_capt();
  puts("---------------------------------");
  cc_capt();
  return 0;
}

【问题讨论】:

  • 不要垃圾标签! C 是一种不同的语言!
  • std::cout.rdbuf(ss.rdbuf()),但实际上你会定义 int cc_capt(std::ostream&amp;) 并传入流。
  • @user657267,如果您无法访问所有功能(例如cc_hw()),那么捕获std::cout 是一个不错的方法。我在一些单元测试中这样做,其中某些功能只是在控制台中打印消息。

标签: c++ iostream


【解决方案1】:

可以,但不能交换整个流——只是交换流缓冲区。

void cc_capt() {
    using namespace std;

    stringstream ss;

    auto orig = std::cout.rdbuf(ss.rdbuf());

    hw_cc();

    std::cout.rdbuf(orig);

    std::cout << "captured: " << ss.str() << "\n";
}

请注意,在这种情况下,我们并没有真正使用stringstream 本身,只是它包含的stringbuf。如果我们愿意,我们可以定义一个 basic_stringbuf&lt;char&gt; 并直接使用它,而不是定义一个 stringstream 然后只使用它包含的 stringbuf

【讨论】:

    【解决方案2】:

    根据 Jerry 的示例,我编写了一个模板,它有一个很大的优势,它是安全的(即,如果发生异常,您的缓冲区会自动恢复。)

    这样使用:

    {
      ostream_to_buf<char> buf(std::cout);
    
      ... run code which `std::cout << "data"` ...
    
      std::string const output(buf.str());
    
      ... do something with `output` ...
    }  // <-- here the buffer is restored
    

    这是我认为非常接近 STL 的功能模板。模板本身就是一个std::stringbuf,它把自己插入到构造函数中。析构函数恢复原始缓冲区,因此它是异常安全的。

    template<
          class CharT
        , class Traits = std::char_traits<CharT>
        , class Allocator = std::allocator<CharT>
    >
    class ostream_to_buf
        : public std::basic_stringbuf<CharT, Traits, Allocator>
    {
    public:
        typedef CharT                       char_type;
        typedef Traits                      traits_type;
        typedef typename Traits::int_type   int_type;
        typedef typename Traits::pos_type   pos_type;
        typedef typename Traits::off_type   off_type;
        typedef Allocator                   allocator_type;
    
        typedef std::basic_stringbuf<char_type, traits_type, allocator_type>    stringbuf_type;
        typedef std::basic_ostream<char_type, traits_type>                      stream_type;
        typedef std::basic_streambuf<char_type, traits_type>                    streambuf_type;
        typedef std::basic_string<char_type, traits_type, allocator_type>       string_type;
    
        ostream_to_buf<char_type, traits_type, allocator_type>(stream_type & out)
            : f_stream(out)
            , f_original(f_stream.rdbuf(this))
        {
        }
    
        ostream_to_buf<char_type, traits_type, allocator_type>(ostream_to_buf<char_type, traits_type, allocator_type> const & rhs) = delete;
        ostream_to_buf<char_type, traits_type, allocator_type> & operator = (ostream_to_buf<char_type, traits_type, allocator_type> const & rhs) = delete;
    
        ~ostream_to_buf()
        {
            f_stream.rdbuf(f_original);
        }
    
    private:
        stream_type &       f_stream;
        streambuf_type *    f_original = nullptr;
    };
    

    复制构造函数和赋值运算符被删除,因为它们在这种情况下不起作用。

    您可能可以使其与 C++11 甚至 C++03 一起使用。我有 C++14,但我认为这些都不需要 C++14。

    【讨论】: