在我的应用程序中,我想将通常转到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
答案 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和其他一些取决于平台的