微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

捕获功能标准输出并将其写入文件

如何解决捕获功能标准输出并将其写入文件

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

Foo

我需要从 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();
}

在这里您可以看到它按预期运行: Demo


要克服函数签名变化的问题,可以使用委托的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;
}

Demo


您甚至可以使用一个小的帮助程序类来做到这一点,该类可以概括任何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();
}

Demo


关于您的comment

可以,但是我将需要一些东西来存储这些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_,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,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;
}

当然还有 Demo

,

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,folks\n";

请注意,此处未调用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内容输出/附加到您的文件中,然后清除ss以供重用。

您还可以调用1个测试函数,将ss内容输出到唯一的文件,然后清除ss并执行下一个测试函数,等等。

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

接下来,考虑将stringstream保留在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)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。