7

I'm writing a code to analyze PDF file. I want to display the output on the console as well as to have a copy of the output in a file, I used this code save the output in a file:

import sys
sys.stdout = open('C:\\users\\Suleiman JK\\Desktop\\file.txt',"w")
print "test"

but could I display the output into console as well but without using classes because I'm not good with them?

K DawG
  • 13,287
  • 9
  • 35
  • 66
user3737563
  • 97
  • 1
  • 1
  • 4
  • You can store the output on a file with `f.write(data)` and then print to the console with `print(data)`. I don't understand the classes reference in the question. – llrs Jun 13 '14 at 12:15
  • 2
    Python [logging](https://docs.python.org/2/library/logging.html) module comes to the rescue! – Maciej Gol Jun 13 '14 at 12:16
  • @Llopis I mean I know how to work with classes. If I want to use that in my example above how could I use write – user3737563 Jun 13 '14 at 12:22
  • With a method of class X that does that. `X.write(): .....` where .... are the code you posted or any variant of it. Although I don't know why you want to do so the logging package could solve also your problems. – llrs Jun 13 '14 at 12:28

4 Answers4

7

(This answer uses Python 3 and you will have to adapt it if you prefer Python 2.)

Start by importing the Python logging package (and sys for accessing the standard output stream):

import logging
import sys

In your entry point, set up a handler for both the standard output stream and your output file:

targets = logging.StreamHandler(sys.stdout), logging.FileHandler('test.log')

and configure the logging package to output only the message without the log level:

logging.basicConfig(format='%(message)s', level=logging.INFO, handlers=targets)

Now you can use it:

>>> logging.info('testing the logging system')
testing the logging system
>>> logging.info('second message')
second message
>>> print(*open('test.log'), sep='')
testing the logging system
second message
Adam
  • 15,537
  • 2
  • 42
  • 63
5

sys.stdout can point to any object that has a write method, so you can create a class that writes to both a file and the console.

import sys

class LoggingPrinter:
    def __init__(self, filename):
        self.out_file = open(filename, "w")
        self.old_stdout = sys.stdout
        #this object will take over `stdout`'s job
        sys.stdout = self
    #executed when the user does a `print`
    def write(self, text): 
        self.old_stdout.write(text)
        self.out_file.write(text)
    #executed when `with` block begins
    def __enter__(self): 
        return self
    #executed when `with` block ends
    def __exit__(self, type, value, traceback): 
        #we don't want to log anymore. Restore the original stdout object.
        sys.stdout = self.old_stdout

print "Entering section of program that will be logged."
with LoggingPrinter("result.txt"):
    print "I've got a lovely bunch of coconuts."
print "Exiting logged section of program."

Result:

Console:

Entering section of program that will be logged.
I've got a lovely bunch of coconuts.
Exiting logged section of program.

result.txt:

I've got a lovely bunch of coconuts.

This method may be preferable to codesparkle's in some circumstances, because you don't have to replace all your existing prints with logging.info. Just put everything you want logged into a with block.

Kevin
  • 74,910
  • 12
  • 133
  • 166
4

You could make a function which prints both to console and to file. You can either do it by switching stdout, e.g. like this:

def print_both(file, *args):
    temp = sys.stdout #assign console output to a variable
    print ' '.join([str(arg) for arg in args]) 
    sys.stdout = file 
    print ' '.join([str(arg) for arg in args])
    sys.stdout = temp #set stdout back to console output

or by using file write method (I suggest using this unless you have to use stdout)

def print_both(file, *args):
    toprint = ' '.join([str(arg) for arg in args])
    print toprint
    file.write(toprint)

Note that:

  1. The file argument passed to the function must be opened outside of the function (e.g. at the beginning of the program) and closed outside of the function (e.g. at the end of the program). You should open it in append mode.
  2. Passing *args to the function allows you to pass arguments the same way you do to a print function. So you pass arguments to print...

...like this:

print_both(open_file_variable, 'pass arguments as if it is', 'print!', 1, '!')

Otherwise, you'd have to turn everything into a single argument i.e. a single string. It would look like this:

 print_both(open_file_variable, 'you should concatenate'+str(4334654)+'arguments together')

I still suggest you learn to use classes properly, you'd benefit from that very much. Hope this helps.

Tommaso Di Noto
  • 1,208
  • 1
  • 13
  • 24
user3725459
  • 414
  • 3
  • 9
1

I was too lazy to write a function, so when I needed to print to both the console and file I wrote this quick and (not so) dirty code:

import sys
...
with open('myreport.txt', 'w') as f:
    for out in [sys.stdout, f]:
        print('some data', file=out)
        print('some mre data', file=out)
Israel Unterman
  • 13,158
  • 4
  • 28
  • 35