Using freopen() to print to file and screen
Asked Answered
B

2

12

I am trying to use freopen() to print to a text file and the screen, but I am only achieving the printing to a file.

I was wondering if there was an easy to save the programs output to a file and print it to the screen? Because I had this working another way, but I ended up having to print out every statement twice. One being for the file the other just for the output.

Note: I am new to C++ and I am trying to learn it for a class next semester so direct answer are needed as I have already look online and couldn't find any simple answers to this solution besides.

Here is what I have so far:

#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;
}
Bloomington answered 20/12, 2012 at 18:43 Comment(2)
+1 for well formed question.Guaiacum
You will still have to write everything twice. freopen(..., stdout) will REPLACE the current destination of stdout with the file(handle) of the first argument of freopen(). A file (or stream) in C/C++ can only go to one place. As beginner, I'm not sure there is an EASY way to write code that outputs to two places. The way I would do it is by implementing my own variant of ostream that writes to two places at once. But that's hardly beginner coding.Wizen
N
7

I don't know a simple way to achieve that, but I've managed to solve this somehow.

Using fstreams you can output to file the same way you can write to console.

#include <fstream>

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

Now there's a point we can start: is there a way we can output to the console and file simultaneously?

I've recently written stream demultiplexer to address that problem:

#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);
    }
};

You can use it like this:

stream_demultiplexer spl;
std::ofstream f("file.txt");
spl.add_stream(f);
spl.add_stream(std::cout);
spl << 55 << " HELLO WORLD";

My approach has advantage that manipulators and unformatted output works correctly:

spl << 76 << " " << std::hex << 76 << std::endl;
spl.put('a');
spl.write("ABCDE", 5);
Novelia answered 20/12, 2012 at 18:59 Comment(1)
Thank you very much for the help. To bad there isn't a simpler way though.Bloomington
N
5

The easy way in a UNIX-like environment is to use the shell command tee:

$ my-program | tee output.txt

will copy stdout to the terminal, and also to the file output.txt.


If you have to do it in code, you could use your own output stream instead of cout, which forwards every operator<< to two (or more) ostreams. This feels nicer (to me) than mucking around with the C output file underlying the C++ ostream cout.

#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;
}

Then, if you replace your freopen line with:

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

you can just use tee << instead of cout <<.

Note that you'll either need to pass tee into your functions, or make it a global for that to work.

Northbound answered 20/12, 2012 at 18:58 Comment(1)
Note also that you can't tee << endl; so you'll have to use '\n' and implement a Tee.flush() if you want that functionality.Progeny

© 2022 - 2024 — McMap. All rights reserved.