使用 freopen() 输出到文件和屏幕

12
我试图使用 freopen() 将文本打印到文件和屏幕,但我只能将其打印到文件中。
我想知道有没有一种简单的方法将程序输出保存到文件并打印到屏幕上?因为我曾以另一种方式工作过,但最终不得不每个语句都打印两次。一个是用于文件,另一个只是用于输出。
注意:我是C++的新手,并且正在尝试为下个学期的课程学习它,所以需要直接的答案,因为我已经在网上查找了很多,但除此之外没有找到任何简单的解决方案。
以下是我目前拥有的内容:
#include<iostream>
#include<time.h>
#include<stdlib.h>
#include<fstream>

using namespace std; 

void menu(){
    cout << "\t********************************************************\n"
         << "\t* Welcome to slot machine.                             *\n"
         << "\t* Would you like to play? (1 to play, 2 not to play)   *\n"
         << "\t********************************************************\n\n";
    return;
}

void update(int arr[], int &token) {
    if (arr[0]==arr[1] && arr[1]==arr[2]) {
        token+=4;
        cout << "You win\n\n";
    } else if (arr[0]==arr[1] || arr[1]==arr[2] || arr[0]==arr[2]) {
        token+=1;
        cout << "You got two out of three\n\n";
    } else {
        token-=1;
        cout << "You lose\n\n";
    }
}

int main() {
    freopen("file.txt", "w", stdout);
    int x, arr[3], token=4;
    srand(time(0));
    menu();
    cin >> x;
    while(token!=0) {
        cout << "You have " << token << " tokens\n\n"
             << "Pull? (1 to pull, 2 not to pull)\n\n";
        cin>>x;
        if(x==1) {
            for(int i=0; i<3; i++) {
                arr[i]=1+rand()%10;
            }
            cout << "\t\t";
            for(int j=0; j<3; j++) {
                cout << arr[j] << " ";
            }
            cout << "\n\n";
            update(arr,token);
        }
        else{  
            cout << "OK\n";
        }
    }
    cin.get();
    return 0;
}

良好的问题,加一分。 - Dillon Benson
1
你仍然需要写两次所有内容。freopen(..., stdout)将用freopen()的第一个参数文件句柄替换stdout的当前输出目标。在C/C++中,一个文件(或流)只能输出到一个地方。作为初学者,我不确定是否有一种简单的方法来编写可以输出到两个位置的代码。我会实现自己的ostream变体,同时写入两个位置。但这并不是初学者编程的好方法。 - Mats Petersson
2个回答

6

我不知道一个简单的方法来实现这一点,但是我已经设法解决了这个问题。

使用fstreams,您可以像写入控制台一样将输出写入文件。

#include <fstream>

int main()
{
     std::ofstream f("file.txt");
     f << "something";
}

现在我们可以开始讨论一个问题:是否有办法同时将输出内容输出到控制台和文件中?

我最近编写了一个流分离器来解决这个问题:

#include <vector>
#include <ostream>
class stream_demultiplexer
{
private:
    typedef std::vector<std::ostream*> str_cont;
    str_cont d;
public:
    stream_demultiplexer& put(std::ostream::char_type ch)
    {
        for(str_cont::iterator it = d.begin(); it != d.end(); ++it)
            (*it)->put(ch);
        return *this;
    }

    stream_demultiplexer& write(const std::ostream::char_type* s, std::streamsize count)
    {
        for(str_cont::iterator it = d.begin(); it != d.end(); ++it)
            (*it)->write(s, count);
        return *this;
    }

    stream_demultiplexer& flush()
    {
        for(str_cont::iterator it = d.begin(); it != d.end(); ++it)
            (*it)->flush();
        return *this;
    }


    template<typename T>
    stream_demultiplexer& operator<<( const T& obj )
    {
        for(str_cont::iterator it = d.begin(); it != d.end(); ++it)
            (**it) << obj;
        return *this;
    }

    stream_demultiplexer& operator<<(std::ios_base& (*func)(std::ios_base&))
    {
        for(str_cont::iterator it = d.begin(); it != d.end(); ++it)
            (**it) << func;
        return *this;
    }

    template<typename CharT, typename Traits>
    stream_demultiplexer& operator<<(std::basic_ios<CharT,Traits>& (*func)(std::basic_ios<CharT,Traits>&) )
    {
        for(str_cont::iterator it = d.begin(); it != d.end(); ++it)
            (**it) << func;
        return *this;
    }

    stream_demultiplexer& operator<<(std::ostream& (*func)(std::ostream&) )
    {
        for(str_cont::iterator it = d.begin(); it != d.end(); ++it)
            (**it) << func;
        return *this;
    }

    void add_stream(std::ostream& ss)
    {
        d.push_back(&ss);
    }
};

你可以像这样使用它:
stream_demultiplexer spl;
std::ofstream f("file.txt");
spl.add_stream(f);
spl.add_stream(std::cout);
spl << 55 << " HELLO WORLD";

我的方法的优点是可以正确操作操纵器和未格式化的输出:

spl << 76 << " " << std::hex << 76 << std::endl;
spl.put('a');
spl.write("ABCDE", 5);

1
非常感谢您的帮助。可惜没有更简单的方法。 - Bob

5
在类UNIX环境下的简便方法是使用shell命令tee:
$ my-program | tee output.txt

将标准输出复制到终端,并同时复制到文件output.txt


如果你必须在代码中这样做,你可以使用自己的输出流代替cout,它会将每个operator<<转发给两个(或更多)ostreams。对我来说,这比操纵C++ ostream cout底层的C输出文件更好。

#include <ostream>

class Tee {
    std::ostream &first, &second;

    template<typename T> friend Tee& operator<< (Tee&, T);

public:
    Tee(std::ostream &f, std::ostream &s) : first(f), second(s) {}
};

template <typename T>
Tee& operator<< (Tee &t, T val)
{
    t.first << val;
    t.second << val;
    return t;
}

如果你用以下代码替换freopen代码:

std::ofstream outfile("file.txt");
Tee tee(std::cout, outfile);

你可以使用tee <<代替cout <<

请注意,您需要将tee传递给您的函数,或使其成为全局变量才能正常工作。


请注意,您不能使用 tee << endl;,因此必须使用 '\n' 并实现 Tee.flush() 如果您想要该功能。 - feetwet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接