我可以将标准输出重定向到某种字符串缓冲区吗?

2024-12-18 08:39:00
admin
原创
136
摘要:问题描述:我正在使用 pythonftplib编写一个小型 FTP 客户端,但包中的某些函数不返回字符串输出,而是打印到stdout。我想重定向stdout到一个我可以从中读取输出的对象。我知道stdout可以重定向到任何常规文件:stdout = open("file", "a...

问题描述:

我正在使用 pythonftplib编写一个小型 FTP 客户端,但包中的某些函数不返回字符串输出,而是打印到stdout。我想重定向stdout到一个我可以从中读取输出的对象。

我知道stdout可以重定向到任何常规文件:

stdout = open("file", "a")

但我更喜欢不使用本地驱动器的方法。

我正在寻找类似BufferedReaderJava 的东西,可以用来将缓冲区包装到流中。


解决方案 1:

from cStringIO import StringIO # Python3 use: from io import StringIO
import sys

old_stdout = sys.stdout
sys.stdout = mystdout = StringIO()

# blah blah lots of code ...

sys.stdout = old_stdout

# examine mystdout.getvalue()

解决方案 2:

Python 3.4+中有一个contextlib.redirect_stdout()函数:

import io
from contextlib import redirect_stdout

with io.StringIO() as buf, redirect_stdout(buf):
    print('redirected')
    output = buf.getvalue()

下面是一个代码示例,展示了如何在旧版本 Python 上实现它。

解决方案 3:

只需补充一下上面 Ned 的回答:您可以使用它将输出重定向到任何实现了 write(str) 方法的对象

这可以很好地用于在 GUI 应用程序中“捕获” stdout 输出。

这是 PyQt 中的一个愚蠢的例子:

import sys
from PyQt4 import QtGui

class OutputWindow(QtGui.QPlainTextEdit):
    def write(self, txt):
        self.appendPlainText(str(txt))

app = QtGui.QApplication(sys.argv)
out = OutputWindow()
sys.stdout=out
out.show()
print "hello world !"

解决方案 4:

python3 的上下文管理器:

import sys
from io import StringIO


class RedirectedStdout:
    def __init__(self):
        self._stdout = None
        self._string_io = None

    def __enter__(self):
        self._stdout = sys.stdout
        sys.stdout = self._string_io = StringIO()
        return self

    def __exit__(self, type, value, traceback):
        sys.stdout = self._stdout

    def __str__(self):
        return self._string_io.getvalue()

像这样使用:

>>> with RedirectedStdout() as out:
>>>     print('asdf')
>>>     s = str(out)
>>>     print('bsdf')
>>> print(s, out)
'asdf
' 'asdf
bsdf
'

解决方案 5:

从 Python 2.6 开始,您可以使用任何实现io.TextIOBaseAPI 的东西作为替代品。此解决方案还使您能够sys.stdout.buffer.write()在 Python 3 中使用将(已)编码的字节字符串写入标准输出(请参阅Python 3 中的 stdout)。使用io.StringIO将不起作用,因为sys.stdout.buffernorsys.stdout.encoding都不可用。

使用以下解决方案io.TextIOWrapper

import io
import sys

# Setup stdout.
old_stdout = sys.stdout
sys.stdout = io.TextIOWrapper(io.BytesIO(), sys.stdout.encoding)

# Write to stdout or stdout.buffer.
...

# Read from stdout.
sys.stdout.seek(0)
out = sys.stdout.read()

# Restore stdout.
sys.stdout.close()
sys.stdout = old_stdout

此解决方案适用于 Python 2 >= 2.6 和 Python 3。

请注意,新的sys.stdout.write()仅接受unicode字符串,并且sys.stdout.buffer.write()仅接受字节字符串。无需更改即可在Python 2和3上运行的代码通常使用sys.stdout.buffer

因此,为了同时sys.stdout.write()接受 unicode 和字节字符串,您可以使用这个io.TextIOWrapper子类:

class StdoutBuffer(io.TextIOWrapper):

    def write(self, string):
        try:
            return super(StdoutBuffer, self).write(string)
        except TypeError:
            # Redirect encoded byte strings directly to buffer.
            return super(StdoutBuffer, self).buffer.write(string)

您不必设置缓冲区的编码sys.stdout.encoding,但这在使用此方法测试/比较脚本输出时会有所帮助。

解决方案 6:

即使发生异常,此方法也会恢复 sys.stdout。它还会获取发生异常之前的任何输出。

import io
import sys

real_stdout = sys.stdout
fake_stdout = io.BytesIO()   # or perhaps io.StringIO()
try:
    sys.stdout = fake_stdout
    # do what you have to do to create some output
finally:
    sys.stdout = real_stdout
    output_string = fake_stdout.getvalue()
    fake_stdout.close()
    # do what you want with the output_string

在 Python 2.7.10 中测试使用 io.BytesIO()

在 Python 3.6.4 中测试使用io.StringIO()


Bob,如果您觉得修改/扩展的代码实验中的任何内容可能会变得有趣,请添加一个案例,否则请随意删除它

Ad informandum ... 在寻找一些可行的机制来“抓取”输出的过程中,通过扩展实验得出的一些评论,numexpr.print_versions()直接针对<stdout>(需要清理 GUI 并将详细信息收集到调试报告中)

# THIS WORKS AS HELL: as Bob Stein proposed years ago:
#  py2 SURPRISEDaBIT:
#
import io
import sys
#
real_stdout = sys.stdout                        #           PUSH <stdout> ( store to REAL_ )
fake_stdout = io.BytesIO()                      #           .DEF FAKE_
try:                                            # FUSED .TRY:
    sys.stdout.flush()                          #           .flush() before
    sys.stdout = fake_stdout                    #           .SET <stdout> to use FAKE_
    # ----------------------------------------- #           +    do what you gotta do to create some output
    print 123456789                             #           + 
    import  numexpr                             #           + 
    QuantFX.numexpr.__version__                 #           + [3] via fake_stdout re-assignment, as was bufferred + "late" deferred .get_value()-read into print, to finally reach -> real_stdout
    QuantFX.numexpr.print_versions()            #           + [4] via fake_stdout re-assignment, as was bufferred + "late" deferred .get_value()-read into print, to finally reach -> real_stdout
    _ = os.system( 'echo os.system() redir-ed' )#           + [1] via real_stdout                                 + "late" deferred .get_value()-read into print, to finally reach -> real_stdout, if not ( _ = )-caught from RET-d "byteswritten" / avoided from being injected int fake_stdout
    _ = os.write(  sys.stderr.fileno(),         #           + [2] via      stderr                                 + "late" deferred .get_value()-read into print, to finally reach -> real_stdout, if not ( _ = )-caught from RET-d "byteswritten" / avoided from being injected int fake_stdout
                       b'os.write()  redir-ed' )#  *OTHERWISE, if via fake_stdout, EXC <_io.BytesIO object at 0x02C0BB10> Traceback (most recent call last):
    # ----------------------------------------- #           ?                              io.UnsupportedOperation: fileno
    #'''                                                    ? YET:        <_io.BytesIO object at 0x02C0BB10> has a .fileno() method listed
    #>>> 'fileno' in dir( sys.stdout )       -> True        ? HAS IT ADVERTISED,
    #>>> pass;            sys.stdout.fileno  -> <built-in method fileno of _io.BytesIO object at 0x02C0BB10>
    #>>> pass;            sys.stdout.fileno()-> Traceback (most recent call last):
    #                                             File "<stdin>", line 1, in <module>
    #                                           io.UnsupportedOperation: fileno
    #                                                       ? BUT REFUSES TO USE IT
    #'''
finally:                                        # == FINALLY:
    sys.stdout.flush()                          #           .flush() before ret'd back REAL_
    sys.stdout = real_stdout                    #           .SET <stdout> to use POP'd REAL_
    sys.stdout.flush()                          #           .flush() after  ret'd back REAL_
    out_string = fake_stdout.getvalue()         #           .GET string           from FAKE_
    fake_stdout.close()                         #                <FD>.close()
    # +++++++++++++++++++++++++++++++++++++     # do what you want with the out_string
    #
    print "
{0:}
{1:}{0:}".format( 60 * "/\\\",# "LATE" deferred print the out_string at the very end reached -> real_stdout
                                     out_string #                   
                                     )
'''
PASS'd:::::
...
os.system() redir-ed
os.write()  redir-ed
////////////////////////////////////////////////////////////\n123456789
'2.5'
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Numexpr version:   2.5
NumPy version:     1.10.4
Python version:    2.7.13 |Anaconda 4.0.0 (32-bit)| (default, May 11 2017, 14:07:41) [MSC v.1500 32 bit (Intel)]
AMD/Intel CPU?     True
VML available?     True
VML/MKL version:   Intel(R) Math Kernel Library Version 11.3.1 Product Build 20151021 for 32-bit applications
Number of threads used by default: 4 (out of 4 detected cores)
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
////////////////////////////////////////////////////////////\n>>>

EXC'd :::::
...
os.system() redir-ed
////////////////////////////////////////////////////////////\n123456789
'2.5'
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Numexpr version:   2.5
NumPy version:     1.10.4
Python version:    2.7.13 |Anaconda 4.0.0 (32-bit)| (default, May 11 2017, 14:07:41) [MSC v.1500 32 bit (Intel)]
AMD/Intel CPU?     True
VML available?     True
VML/MKL version:   Intel(R) Math Kernel Library Version 11.3.1 Product Build 20151021 for 32-bit applications
Number of threads used by default: 4 (out of 4 detected cores)
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
////////////////////////////////////////////////////////////\n
Traceback (most recent call last):
  File "<stdin>", line 9, in <module>
io.UnsupportedOperation: fileno
'''

解决方案 7:

在 Python3.6 中,StringIOcStringIO模块已经消失,你应该使用io.StringIO。所以你应该像第一个答案那样这样做:

import sys
from io import StringIO

old_stdout = sys.stdout
old_stderr = sys.stderr
my_stdout = sys.stdout = StringIO()
my_stderr = sys.stderr = StringIO()

# blah blah lots of code ...

sys.stdout = self.old_stdout
sys.stderr = self.old_stderr

// if you want to see the value of redirect output, be sure the std output is turn back
print(my_stdout.getvalue())
print(my_stderr.getvalue())

my_stdout.close()
my_stderr.close()

解决方案 8:

使用pipe()并写入适当的文件描述符。

https://docs.python.org/library/os.html#file-descriptor-operations

解决方案 9:

这是对此的另一种看法。 正如文档所述contextlib.redirect_stdout, with很棒,但对于日常使用来说仍然有点冗长。以下是如何通过子类化使其成为一行代码:io.StringIO()`contextlib.redirect_stdout`

import sys
import io
from contextlib import redirect_stdout

class capture(redirect_stdout):

    def __init__(self):
        self.f = io.StringIO()
        self._new_target = self.f
        self._old_targets = []  # verbatim from parent class

    def __enter__(self):
        self._old_targets.append(getattr(sys, self._stream))  # verbatim from parent class
        setattr(sys, self._stream, self._new_target)  # verbatim from parent class
        return self  # instead of self._new_target in the parent class

    def __repr__(self):
        return self.f.getvalue()  

由于 enter 返回 self,因此在 with 块退出后,上下文管理器对象可用。此外,由于 repr 方法,上下文管理器对象的字符串表示实际上是 stdout。所以现在你有了,

with capture() as message:
    print('Hello World!')
print(str(message)=='Hello World!
')  # returns True
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用