捕获函数标准输出并将其写入文件



我尝试做的是将函数内的所有输出写入文件。也许我需要一种方法来test_func将所有输出(不仅是数组)分配给某种变量,以便我可以返回它,但我无法弄清楚。

#include <iostream>
#include <fstream>
#include <functional>
using namespace std;
void test_func()
{
int a[] = {20,42,41,40};
int b[] = {2,4,2,1};
cout << "Below is the result: "<< endl;
for (int i=0; i<4; i++){
cout << "***********************" << endl;
cout << a[i] << " : " << b[i] <<endl;
cout << "-----------------------" << endl;
}
}
void write_to_file(function<void()>test_func)
{
ofstream ofile;
ofile.open("abc.txt");
ofile << test_func();  // This is not allowed
ofile.close();
}
int main()
{
write_to_file(test_func);
return 0;
}

我需要从test_func获取所有输出,而不仅仅是数组 a 和 b,因为我有多个不同格式的函数,使用相同的函数write_to_file写入文件都需要这些函数。

有什么合乎逻辑的方法可以做到这一点吗?(或功能的替代品?

这里有一些代码可以按照你想要的方式工作。您必须将std::cout当前的rdbuf()替换为其中一个文件流,然后重置它:

void write_to_file(function<void()>test_func) {
ofstream ofile;
ofile.open("abc.txt");
std::streambuf* org = cout.rdbuf(); // Remember std::cout's old state
cout.rdbuf(ofile.rdbuf()); // Bind it to the output file stream

test_func(); // Simply call the anonymous function
cout.rdbuf(org); // Reset std::cout's old state
ofile.close();
}

在这里,您可以看到它按预期运行:演示


要克服不同函数签名的问题,您可以使用委派 lambda 函数:

void test_func2(double a, int b) {
cout << a  << " * " << b << " = " << (a * b) << endl;
}
int main() {
// Create a lambda function that calls test_func2 with the appropriate parameters
auto test_func_wrapper = []() {
test_func2(0.356,6);
};
write_to_file(test_func_wrapper); // <<<<< Pass the lambda here
// You can also forward the parameters by capturing them in the lambda definition
double a = 0.564;
int b = 4;
auto test_func_wrapper2 = [a,b]() {
test_func2(a,b);
};
write_to_file(test_func_wrapper2);
return 0;
}

演示


您甚至可以使用一个小的帮助程序类来执行此操作,该类概括了任何std::ostream类型的情况:

class capture {
public:
capture(std::ostream& out_, std::ostream& captured_) : out(out_), captured(captured_), org_outbuf(captured_.rdbuf()) {
captured.rdbuf(out.rdbuf());
}
~capture() {
captured.rdbuf(org_outbuf);
}
private:
std::ostream& out;
std::ostream& captured;
std::streambuf* org_outbuf;
};
void write_to_file(function<void()>test_func)
{
ofstream ofile;
ofile.open("abc.txt");
{
capture c(ofile,cout); // Will cover the current scope block
test_func();
}
ofile.close();
}

演示


所以关于你的评论:

当然,但是我需要一些东西来存储这些cout,或者也许还有另一种完全不同的方法而不是使用test_func()进行该过程?

我们现在手头有一切可以做到这一点

#include <iostream>
#include <fstream>
#include <functional>
#include <string>
#include <sstream>
using namespace std;
void test_func1(const std::string& saySomething) {
cout << saySomething << endl;
}
void test_func2(double a, int b) {
cout << "a * b = " << (a * b) << endl;
}
class capture {
public:
capture(std::ostream& out_, std::ostream& captured_) : out(out_), captured(captured_), org_outbuf(captured_.rdbuf()) {
captured.rdbuf(out.rdbuf());
}
~capture() {
captured.rdbuf(org_outbuf);
}
private:
std::ostream& out;
std::ostream& captured;
std::streambuf* org_outbuf;
};
int main() {
std::string hello = "Hello World";
auto test_func1_wrapper = [hello]() {
test_func1(hello);
};
double a = 0.356;
int b = 6;
auto test_func2_wrapper = [a,b]() {
test_func2(a,6);
};
std::stringstream test_func1_out;
std::stringstream test_func2_out;
std::string captured_func_out;

{   capture c(test_func1_out,cout);
test_func1_wrapper();
}
{   capture c(test_func2_out,cout);
test_func2_wrapper();
}
captured_func_out = test_func1_out.str();
cout << "test_func1 wrote to cout:" << endl;
cout << captured_func_out << endl;
captured_func_out = test_func2_out.str();
cout << "test_func2 wrote to cout:" << endl;
cout << captured_func_out << endl;
}

当然还有演示

ofile << test_func();行表示被调用test_func();的返回值被定向到该流。它不会对调用的函数中执行的操作执行任何操作。不过,您可以将流传递给函数。

void test_func(ostream& outs)
{
outs << "Below is the result: "<< endl;
}

并用coutofile调用它 - 任何ostream作为参数。

void write_to_file(function<void(ostream&)>test_func)
{
ofstream ofile;
ofile.open("abc.txt");
test_func(ofile);  // This is not allowed
ofile.close();
}

但是,如果作为流操纵器的功能行为是你想要的,你必须设计一个合适的运算符。

ostream& operator<< (ostream& o, void(*func)(ostream&) )
{
func(o); 
return o;
}

然后你可以写一些类似的东西

cout << test_func << " That's all, folksn";

请注意,这里没有调用test_func,其 id 用作表达式会导致函数的地址传递给operator<<

真正的流操纵器(例如 https://en.cppreference.com/w/cpp/io/manip/setw)不是作为函数实现的,而是作为函数对象的模板实现的,setw 的参数在行中:

is >> std::setw(6) >> arr;

实际上是构造函数的参数

我尝试做的是将函数中的所有输出写入文件。

我经常使用 std::stringstream 作为文本的临时存储库,即 ss 保存并将所有输出捆绑到"缓冲区"(文本字符串)中,以便延迟输出到文件。

对于您的test_func,您可以添加一个 ss 引用参数:

void test_func(std::stringsteam& ss)
{
int a[] = {20,42,41,40};
int b[] = {2,4,2,1};
cout << "Below is the result: "<< endl;
for (int i=0; i<4; i++){
ss << "***********************" << endl;
ss << a[i] << " : " << b[i] <<endl;
ss << "-----------------------" << endl;
}
}

std::stringstream 本质上是一个基于 ram 的 ofile(没有硬盘开销)。

因此,您可以运行许多test_func,将所有输出集中到一个 ss,然后将 ss 内容清空到一个文件中。

或者,您可以调用 1 test_func,将该 ss 内容输出/附加到您的 ofile,然后清除 ss 以供重复使用。

您还可以调用 1 个测试函数,将该 ss 内容输出到唯一的 ofile,然后清除 ss 并执行下一个测试函数,依此类推。

注意:a) std::stringstream 使用一个 std::string作为工作缓冲区,b) std::string 将其数据保存在动态内存中。 我很少担心党卫军有多大。 但是,如果您担心并有估计,则可以轻松地使用保留来设置字符串大小。 知道这个大小将允许您计划控制非常大的输出文件。

接下来,考虑将字符串流排除在test_func之外,而是将其保留在外部数据收集函数中:

void write_to_file(function<void()>test_func)
{
std::stringstream ss; // temporary container
test_func(ss);        // add contributions
test_func2(ss);       // add contributions
test_func3(ss);       // add contributions  
// ... 
test_funcN(ss);       // add contributions  
// when all testing is complete, output concatenated result to single file
ofstream ofile;
ofile.open("abc.txt");
ofile << ss.str(); 
ofile.close();
}
int main()
{
write_to_file(test_func);
return 0;
}

注意:要清空ss,我使用2个步骤:

void ssClr(stringstream& ss) { ss.str(string()); ss.clear(); }
//                             clear data        clear flags

注意:我将编码工作封装到一个或多个 c++ 类中。 在我的代码中,ss 对象被声明为我的类的数据属性,因此该类的所有函数属性都可以访问,包括每个test_funci(即不需要传递 ss)

最新更新