C ++:重定向STDOUT

时间:2011-01-26 21:58:57

标签: c++

在我的应用程序中,我想将通常转到stdout流的输出重定向到我定义的函数。我读到你可以将stdio重定向到一个文件,那么为什么不去一个函数?

例如:

void MyHandler( const char* data );

//<<Magical redirection code>>

printf( "test" );
std::cout << "test" << std::endl;

//MyHandler should have been called with "test" twice, at this point
  • 我如何实现这种/类似行为?

8 个答案:

答案 0 :(得分:39)

@Konrad Rudolph是对的,你可以轻松完成这项任务,至少对于cout / cerr / clog来说。您甚至不需要自己的streambuf实现,只需使用ostringstream。

// Redirect cout.
streambuf* oldCoutStreamBuf = cout.rdbuf();
ostringstream strCout;
cout.rdbuf( strCout.rdbuf() );

// This goes to the string stream.
cout << "Hello, World!" << endl;

// Restore old cout.
cout.rdbuf( oldCoutStreamBuf );

// Will output our Hello World! from above.
cout << strCout.str();

同样的事情适用于cerr和clog,但根据我的经验,一般不适用于stdout / stderr,因此printf不会输出。 cout转到stdout,但重定向cout不会重定向所有stdout。至少,那是我的经历。

如果预计数据量很小,则freopen / setbuf工作正常。我最终做了更好的dup / dup2重定向到管道。

更新:我写了一篇博文,展示了我最终使用的dup2方法,您可以阅读here。它是为OS X编写的,但可能适用于其他Unix版本。我非常怀疑它在Windows中是否可行。可可版同样的东西here

答案 1 :(得分:9)

所有其他答案都错了。您可以执行此操作,您无需诉诸freopen,也不需要使用任何其他C或非标准函数。

相反,您需要创建自己的std::streambuf实现,即您自己的流缓冲区。

完成后,您可以通过切换缓冲区来重定向cout流:

your_stream_buffer new_buffer;
streambuf* old_buffer = cout.rdbuf(&new_buffer);

cout << "Hello"; // This will be redirected to `new_buffer`.

// Restore original buffer:
cout.rdbuf(old_buffer);

由于我自己从未这样做过,所以我无法准确地告诉你streambuf的实现方式。我的建议是查看文档并执行一个虚拟实现,为所有已实现的函数打印诊断。这应该有助于弄清楚班级是如何运作的。

或者,看看您选择的C ++参考书,它描述了streambuf类。

答案 2 :(得分:5)

可以通过取消引用其指针来禁用stdin / stdout:

FILE fp_old = *stdout;  // preserve the original stdout
*stdout = *fopen("/dev/null","w");  // redirect stdout to null
HObject m_ObjPOS = NewLibraryObject(); // call some library which prints unwanted stdout
*stdout=fp_old;  // restore stdout

答案 3 :(得分:4)

答案:是的,你可以通过复制。正如你所谈到的,freopen只会将stdout重新打开到文件中。

查看How to buffer stdout in memory and write it from a dedicated thread

答案 4 :(得分:1)

std::cout对象具有固定含义,即输出到标准输出流。程序的用户可以控制标准输出连接的位置,而不是您。您可以做的是决定是要写入文件,标准输出还是写入任何其他输出流。因此,在您的代码中,您可以切换要写入的流。

同样,写入标准输出流的目的是为用户提供选择输出位置的灵活性。你不应该重新定向标准;这是用户应该有自由做的事情。

另一件事是你不应该在C ++程序中混合使用C IO和C ++ IO。选择您要使用的IO库并坚持使用。

也就是说,你可以在C ++中非常优雅地为一个函数切换流,通过模板std::basic_istream<>的模板参数上的处理函数来获取输入。然后,该函数将从输入流中读取其输入,而与其正在使用的实际流类无关。这是一个例子:

#include<iostream>
#include<fstream>
#include<string>

template<class Ch, class Tr>
void dodge_this(std::basic_istream<Ch, Tr>& in)
{
    // in is an input stream. read from it as you read from std::cin.
}

int main(int argc, char* argv[])
{
    if( std::string(argv[1]) == "cin" ) {
        dodge_this(std::cin);
    } else if( std::string(argv[1]) == "file" ) {
        std::ifstream file("input.txt");
        dodge_this(file);
    } else {
        dodge_this(dev_null_stream);  // i just made that up. you get the idea.
    }
}

答案 5 :(得分:0)

以下不是最好的实现,但它应该适用于大多数输出​​...

using std::cout;
using std::endl;
using std::string;
using std::stringstream;

class Handler {
        public:
                Handler() {
                };
                virtual ~Handler() {
                };
                void operator<<(string str) {
                        this->stream << str;
                };
                string print(void) {
                        return this->stream.str();
                }
        private:
                stringstream stream;
};

int main(int argc, char ** argv) {
        Handler handle;
        handle << argv[1];
        cout << handle.print() << endl;
        return 0;
}

我只是在下面放了一个小的主要功能供您试用,看看它是否按您希望的方式工作..

答案 6 :(得分:0)

另一个选择是将处理程序类调用放入继承的streambuf类中。我需要将cout重定向到对话框中可能有用的对话框中的Win GUI编辑控件。这是课程代码:

//-------------------------------- DlgStringbuf Definition -----------------------     
class DlgStringbuf : public std::stringbuf
{
public:
  DlgStringbuf(void) : _hwndDlg(NULL), _editControlID(0), _accum(""), _lineNum(0) {}

  void SetDlg(HWND dlg, int editControlID)
    { _hwndDlg = dlg; _editControlID = editControlID; }
  void Clear(void)
    { _accum.clear(); _lineNum = 0; }

protected:
  virtual std::streamsize xsputn(const char* s, std::streamsize num) 
  {
    std::mutex m;
    std::lock_guard<std::mutex> lg(m);

    // Prepend with the line number
    std::string str(s, (const uint32_t)num);
    str = std::to_string(_lineNum) + ": " + str + "\r\n";

    // Accumulate the latest text to the front
    _accum = str + _accum;

    // Write to the Win32 dialog edit control.
    if(_hwndDlg != NULL)
      SetDlgItemTextW(_hwndDlg, _editControlID, (LPCWSTR)(std::wstring(_accum.begin(), _accum.end())).c_str());

    _lineNum++;
    return(num);
  }//end xsputn.

private:
  std::string     _accum;
  HWND            _hwndDlg;
  int             _editControlID;
  uint32_t        _lineNum;

};//end DlgStringbuf.

//-------------------------------- DlgStream Definition ------------------------------
class DlgStream : public std::ostream
{
public:
  DlgStream(void) : std::ostream(&_sbuff) {}

  void SetDlg(HWND dlg, int editControlID)
    { _sbuff.SetDlg(dlg, editControlID); }

  void Clear(void)
    { _sbuff.Clear(); }

private:
  DlgStringbuf  _sbuff;
};

...以及在WinMain中,创建对话框及其编辑控件之后的某个位置:

  // Redirect all cout usage to the activity dlg box.
  // Save output buffer of the stream - use unique pointer with deleter that ensures to restore
  // the original output buffer at the end of the program.
  auto del = [&](streambuf* p) {  cout.rdbuf(p); };
  unique_ptr<streambuf, decltype(del)> origBuffer(cout.rdbuf(), del);

  // Redirect the output to the dlg stream.
  _dlgStream.SetDlg(hwndActivityDlg, IDC_EDIT_ACTIVITY);
  _dlgStream.copyfmt(cout);
  cout.rdbuf(_dlgStream.rdbuf());

  cout << "this is from cout";

答案 7 :(得分:-1)

您可以使用sprintf写入字符数组,然后读取值:

char buf[1024];
sprintf(buf, "test");
MyHandler(buf);

还有snprintf和其他一些取决于平台的