【问题标题】:Capture a functions standard output and write it to a file捕获函数标准输出并将其写入文件
【发布时间】:2021-01-06 02:32:21
【问题描述】:

我尝试做的是将函数内的所有输出写入文件。也许我需要一种方法将 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? 写入的输出
  • 对不起。我的意思是说“所有输出”,从“下面是结果:”开始,直到函数结束。
  • 好的,你想通过一个匿名函数作为参数传递给write_to_file()吗?
  • 当然可以,但我需要一些东西来存储这些 cout,或者可能有另一种完全不同的方式,而不是使用 test_func() 来处理该过程?
  • @Omia 还有来自&lt;sstream&gt; 标头的stringstream。它将流存储在内存 i 中,也可以用作先前编写的字符串的 ostream 或源。

标签: c++ function file


【解决方案1】:

ofile &lt;&lt; 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&lt;&lt;

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

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

实际上是构造函数的参数

【讨论】:

  • 你的解决方案都没有做 OP 打算做的事情。
【解决方案2】:

这里有一些代码可以按照您想要的方式工作。您必须将 std::couts current 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_, 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;
}

当然还有 Demo

【讨论】:

    【解决方案3】:

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

    我经常使用 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 的文件(没有硬盘开销)。

    所以你可以运行多个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)

    【讨论】:

      猜你喜欢
      • 2013-03-18
      • 2016-12-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多