如何在程序结束后将控制台输出打印到文本文件中(Python)?

5
我有一个程序,通过print语句将许多计算和结果输出到控制台。我想编写一些代码来将控制台的所有内容导出(或保存)到一个简单的文本文件中。
我在StackOverflow和其他网站上搜索过,但我发现一些方法可以重定向print语句直接输出到文件,但我希望程序正常工作,将输出显示到控制台,然后在程序完成所有操作后保存其内容。
如果有影响,我正在使用Python2.7的PyCharm。

你使用什么操作系统? - Glostas
Windows 10 @Glostas - Mohammad ElNesr
抱歉,我没有很多关于DOS命令的经验,但也许你可以在这里找到一些有用的内容:http://stackoverflow.com/questions/11239924/windows-batch-tee-command。 - Glostas
7个回答

3

好的,通常为了完成这个任务,您需要重写Python内置函数print。但是...有一个叫做iPython的工具可以提供一些钩子。

首先您需要安装iPython

#bash
sudo pip install ipython

我使用sudo命令来简单地定位需要到达的文件夹,继续阅读以下内容。

安装ipython后,你将拥有ipython扩展文件夹,因此进入它:

#bash
cd ~/.ipython/extensions/

在那里创建一个名为print_to_file.py的文件,下面是它的内容:

#python
class PrintWatcher(object):
    def __init__(self, ip):
        self.shell = ip

    def post_execute(self):
        with open('/home/turkus/shell.txt', 'a+') as f:
            in_len = len(self.shell.user_ns['In'])
            i = in_len - 1

            in_ = self.shell.user_ns['In'][i]
            out = self.shell.user_ns['Out'].get(i, '')
            # you can edit this line if you want different input in shell.txt
            f.write('{}\n{}\n'.format(in_, out))


def load_ipython_extension(ip):
    pw = PrintWatcher(ip)
    ip.events.register('post_run_cell', pw.post_execute)

保存文件后,只需运行:

#bash
ipython profile create 

# you will get something like that:
[ProfileCreate] Generating default config file: u'/home/turkus/.ipython/profile_default/ipython_config.py'

现在回到设置我们的钩子。我们必须打开上面路径下创建的ipython_config.py文件,并在其中添加一些魔法(文件中有很多内容,所以请到文件末尾):

# some commented lines here
c = get_config()
c.InteractiveShellApp.extensions = [
    'print_to_file'
]

保存后,您可以运行ipython并编写您的代码。您的每个输入都将被写入到您上面提供路径下的文件中,在我的情况下是:
/home/turkus/shell.txt

注释

你可以通过从 ipython_config.py 文件中的 c.InteractiveShellApp.extensions 列表中删除 'print_to_file' 来避免在每次启动 ipython 时加载扩展。但是请记住,你仍然可以随时加载它,只需在 ipython 控制台中键入:

➜  ~ ipython
Python 2.7.12 (default, Jul  1 2016, 15:12:24) 
Type "copyright", "credits" or "license" for more information.

IPython 4.0.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: %load_ext print_to_file

print_to_file.py 中的任何更改都会在使用 %reload_ext print_to_file 命令后反映在打开的 IPython shell 中,因此您无需退出并再次启动。


感谢@turkus再次的贡献,但是通过所有Python 2.7的打印语句应用它需要太长时间。 - Mohammad ElNesr
1
命令行解决方案不会在 shell 中显示输出,而只会在文件中显示。您需要使用 "tee" 命令。 - Glostas
非常感谢@turkus的巨大努力。我明天早上会在办公室尝试它,并回复给你。 - Mohammad ElNesr
好的,我会等待。 - turkus
@Mohammad ElNesr 两个都是。Glostas提到了另一件事,我已经从我的答案中删除了它,那就是这个命令:python script.py >> shell.txt,它将每个打印输出到一个文件中,但不在控制台中显示。 - turkus
显示剩余4条评论

1
我不确定您如何获取任何编辑器的控制台内容,但是通过将print()语句替换为.write,可以轻松实现此目标。
class Writer(object):
    def __init__(self, out_file, overwrite=False):
        self.file_name = out_file
        self.overwrite = overwrite
        self.history = []

    def write(self, statement):
        self.history.append(statement)
        print statement

    def close(self):
        if self.overwrite:
            self.out_file = open(self.file_name, 'wb')
        else:
            self.out_file = open(self.file_name, 'ab')
        for x in self.history:
            self.out_file.write(x+'/n')
        self.out_file.close()
        self.history = []

p = Writer('my_output_file.txt')
p.write('my string to print and save!') 
p.close() #close the writer to save the contents to a file before exiting

你的答案很棒,但需要重写程序中数百个打印语句。我仍然在想是否有可能在输出之后获取控制台内容? - Mohammad ElNesr
我理解你遇到的问题,会修改我的答案以补充你当前的问题! - TheLazyScripter

1
我理解你的问题后,认为你在寻找tee命令。
python your_program | tee output.txt

这将在控制台和output.txt中同时显示输出。
PS:由于您没有回答我的评论,我假设您使用Linux或MACOS。应该在两者上都能工作。我不知道如何在Windows上做到这一点...

非常抱歉没有及时回复您的评论,因为我有一段时间离开了工作台。不过,我今天已经回答了,并且我使用的是Windows 10操作系统。非常感谢@Glostas。 - Mohammad ElNesr
在Windows 10上是否可以实现这个功能? - Mohammad ElNesr
抱歉,我没有太多的DOS命令经验,但也许你可以在这里找到一些有用的信息: http://stackoverflow.com/questions/11239924/windows-batch-tee-command - Glostas

1
你可以重写print函数,该函数仍然可以通过builtins模块访问。
import builtins

f = open("logs.txt", "w")

def print(*args, sep=' ', end='\n', **kwargs):
    builtins.print(*args, sep=sep, end=end, **kwargs)
    f.write(sep.join(*args) + end)

编辑:Python 2的类似解决方案

from __future__ import print_function

class Print:

    def __init__(self, print_function, filename='test', mode='w'):
        self.print_function = print_function
        self.file = open(filename, 'w')

    def __call__(self, *args, **kwargs):
        self.print_function(*args, **kwargs)
        kwargs['file'] = self.file
        self.print_function(*args, **kwargs)

print = Print(print, 'logs.txt')

这将创建一个print函数,您可以像从__future__导入的函数一样使用它。
完成后关闭文件,您需要运行以下命令:
print.file.close()

这似乎是一个非常好的简洁解决方案,我明天早上会尝试并回复您。非常感谢@odrling。 - Mohammad ElNesr
1
但是你必须记得在结尾关闭文件。 - turkus
尊敬的@odrling, 我尝试了这个解决方案,但它没有起作用!首先,找不到builtins模块(我使用Python 2.7.x),所以我使用了import future_builtins as builtins,然后我写了你的def print...,但是出现了几个错误!我不知道如何应用你的解决方案。非常感谢您能进一步解释一下... - Mohammad ElNesr
抱歉,我没有注意到您使用的是Python 2.7。future_builtins模块不包括打印函数。我认为在Python 2中唯一的解决方案是创建一个函数,并在每次想要使用print时调用此函数。 - odrling
我为Python 2添加了一个解决方案。但这将意味着需要将每个打印语句更改为函数调用,所以我猜你仍然会喜欢你的解决方案。 - odrling

0
也许你应该创建一个变量来记录输出,然后将其放入文件中。
例如:
print statement
logger += statement+"\n" #a new line char so each statement is on a new line

with open('file.txt', 'a') as f:
   f.write(statement)

谢谢您的回答,但这个过程太长了,而且它并不符合我的需求。 - Mohammad ElNesr

0

感谢所有对这个问题做出贡献的人。我最终发现了一个解决方案,只需对原始代码进行最小修改。该解决方案由成员@Status提供,这是它的链接

虽然在发布问题之前我进行了很多搜索,但尊敬的成员的回答使我能够进行精确搜索,特别是@turkus的贡献非常杰出和@Glostas让我认识到“tee”,它指引我找到了我发布的解决方案(尽管它不包括“tee”)。

解决方案在提到的帖子上进行了轻微修改

1- 在程序中添加以下类:

class Logger(object):
"""
Lumberjack class - duplicates sys.stdout to a log file and it's okay
source: https://dev59.com/gHRB5IYBdhLWcg3wc3A0#24583265
"""
def __init__(self, filename="Red.Wood", mode="a", buff=0):
    self.stdout = sys.stdout
    self.file = open(filename, mode, buff)
    sys.stdout = self

def __del__(self):
    self.close()

def __enter__(self):
    pass

def __exit__(self, *args):
    pass

def write(self, message):
    self.stdout.write(message)
    self.file.write(message)

def flush(self):
    self.stdout.flush()
    self.file.flush()
    os.fsync(self.file.fileno())

def close(self):
    if self.stdout != None:
        sys.stdout = self.stdout
        self.stdout = None

    if self.file != None:
        self.file.close()
        self.file = None

2- 在程序开头,在任何打印语句之前,加入以下代码:

my_console = Logger('my_console_file.txt')  # you can change the file's name

3- 在程序结束时,在所有的打印语句之后,加入这一行:

my_console.close()

我测试过了,它完美地运行了,最后在程序结束后,我有了一个控制台输出的克隆。

向大家致以最诚挚的问候,并对所有贡献者表示衷心的感谢。


1
两年后我回到这个问题,现在我看到你提到了我,但是有一个错别字,也许这就是为什么我之前没有注意到这个提醒。无论如何,非常感谢你的答案。 - turkus
抱歉@turkus,我打错了你的名字,已经将你的提及从“turcus”更正为“turkus”。 此致敬礼 - Mohammad ElNesr
这是一个空的注释,但感谢您的回复 ;) - turkus

-1

有一个非常明显但不太优雅的解决方案。

而不是:

print statement 1
calculation
print statement 2

你可以做出类似的东西

sexport =''
calculation
print statement 1
sexport += statement1 + "\n"
calculaztion
print statement 2
sexport += statement 2 

最后将s导出保存到文件中


感谢您的试用,但是它太长了,无法应用于大型程序。 - Mohammad ElNesr

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