用 cout 打印到屏幕很简单, 用 ostream 打印到文件也很简单。
但, 如果一个代码中有多处 cout, 又不想一个一个改成 ostream 对象, 怎样做呢?
如果一部分 cout 希望打印到文件, 另一部分 cout 希望打印到控制台, 又该怎么做呢?
#include
#include
#include
#include
int main()
{
using namespace std;
cout << "hello world\n";
cout << "学习·现代·西佳佳\n";
cout << "__cplusplus is " << std::to_string(__cplusplus) << endl;
auto givemetime = chrono::system_clock::to_time_t(std::chrono::system_clock::now());
cout << ctime(&givemetime) << endl;
return 0;
}
➜ C_C++ git:(main) ✗ clang++ coutToFile.cpp -std=c++11
➜ C_C++ git:(main) ✗ ./a.out
hello world
学习·现代·西佳佳
__cplusplus is 201103
Wed Jun 7 21:31:42 2023s
关键点是, 使用 std::ostream 对象, std::ofstream 对象,以及 std::cout 的 rdbuf()
函数。
提供了两个函数, 互为重载版本。第一个函数不带参数, 是getter, 返回流缓冲。
std::basic_streambuf<CharT, Traits>* rdbuf() const;
Manages the associated stream buffer.
Returns the associated stream buffer. If there is no associated stream buffer, returns a null pointer.
第二个函数带参数, 是setter, 设置流缓冲。
std::basic_streambuf<CharT, Traits>* rdbuf( std::basic_streambuf<CharT, Traits>* sb );
Sets the associated stream buffer to sb. The error state is cleared by calling clear(). Returns the associated stream buffer before the operation. If there is no associated stream buffer, returns a null pointer.
#include
#include
#include
#include
#include // added
int main()
{
using namespace std;
ofstream fout("log.txt"); // added
cout.rdbuf(fout.rdbuf()); // added
cout << "hello world\n";
cout << "学习·现代·西佳佳\n";
cout << "__cplusplus is " << std::to_string(__cplusplus) << endl;
auto givemetime = chrono::system_clock::to_time_t(std::chrono::system_clock::now());
cout << ctime(&givemetime) << endl;
return 0;
}
➜ C_C++ git:(main) ✗ clang++ coutToFile.cpp -std=c++17
➜ C_C++ git:(main) ✗ ./a.out
➜ C_C++ git:(main) ✗ cat log.txt
hello world
学习·现代·西佳佳
__cplusplus is 201703
Wed Jun 7 21:36:46 2023
也就是需要能够控制 cout 输出的位置。
#include
#include
#include
#include
#include // added
int main()
{
using namespace std;
auto stdout_buff = cout.rdbuf();
ofstream fout("log.txt"); // added
cout.rdbuf(fout.rdbuf()); // added
cout << "hello world\n";
cout << "学习·现代·西佳佳\n";
cout.rdbuf(stdout_buff);
cout << "__cplusplus is " << std::to_string(__cplusplus) << endl;
auto givemetime = chrono::system_clock::to_time_t(std::chrono::system_clock::now());
cout << ctime(&givemetime) << endl;
return 0;
}
使用了 RAII 的方式, 获取和恢复 src 的 stream buffer。
如果需要线程安全, 需要对 m_closed
变量加锁。
#include
#include
#include
#include
#include // added
class Redirector
{
public:
Redirector(std::ostream& dst, std::ostream& src) :
m_src(src),
m_sbuf(src.rdbuf(dst.rdbuf())),
m_closed(false)
{}
~Redirector()
{
if (!m_closed)
{
close();
}
}
void close()
{
m_src.rdbuf(m_sbuf);
m_closed = true;
}
private:
std::ostream& m_src;
std::streambuf* const m_sbuf;
bool m_closed;
};
int main()
{
using namespace std;
std::ofstream log("log2.txt");
Redirector r(log, std::cout);
cout << "hello world\n";
cout << "学习·现代·西佳佳\n";
r.close();
cout << "__cplusplus is " << std::to_string(__cplusplus) << endl;
auto givemetime = chrono::system_clock::to_time_t(std::chrono::system_clock::now());
cout << ctime(&givemetime) << endl;
return 0;
}
➜ C_C++ git:(main) ✗ clang++ coutToFile.cpp -std=c++17
➜ C_C++ git:(main) ✗ ./a.out
__cplusplus is 201703
Wed Jun 7 21:51:02 2023
➜ C_C++ git:(main) ✗ cat log2.txt
hello world
学习·现代·西佳佳