Python 2和3兼容的字符串文件编写器

时间:2018-05-01 15:18:37

标签: python python-3.x python-2.7 stdout python-unicode

我创建了一个python Context Manager,它捕获所有输出sys.stdout,例如print(),并将其写入文件。

问题在于我无法使用python 2.7和3.6。

上下文管理器内部使用

self.file_writer = open(self.log_file, 'w', encoding='utf8')

但是当我在Python 2.7中运行它时,

print(u"a test string")

会出现错误消息:

write() argument 1 must be unicode, not str

即使字符串显然是unicode。

如果我将文件更改为

self.file_writer = open(self.log_file, 'wb')

然后它适用于Python 2.7,但不适用于3.6。

我需要做些什么才能让它适用于任何python版本?

以下是经理的摘录:

PATH_PREFIX = "some/path/"
class manager:
    def __init__(self):
        self.log_file = os.path.join(PATH_PREFIX, 'log.txt')
    def __enter__(self):
        # create a file for logging
        self.log_file_stream = open(self.log_file, 'w', encoding='utf8')
        self.log_file_stream.__enter__()
        # redirect stdout to this file
        self.previous_stdout = sys.stdout
        sys.stdout = self.log_file_stream
        return self
    def __exit__(self, etype, value, exception_traceback):
        # stop redirecting stdout to the log file
        sys.stdout = self.previous_stdout
        # close the log file
        self.log_file_stream.__exit__()

2 个答案:

答案 0 :(得分:4)

sys.stdout应该是Python 2中的字节流,但是Python 3中的Unicode流。用于Python 2的print在写入stdout之前将Unicode字符串编码为字节字符串,但是你'已覆盖sys.stdout在Python 2和Python 3中都是Unicode流。

当覆盖sys.stdout时,您需要为Python 2提供字节流,但需要为Python 3提供Unicode流。您可以使用sys.version_info.major来决定支持哪个。

答案 1 :(得分:0)

在阅读了其他答案并了解到python的现有库无法实现这一点之后,我编写了一个替换open()的上下文管理器。它有点笨拙,但它完成了工作。

可以通过显式检查python版本而不是使用hasattr()来改进它,但是当我写这篇文章时我并不知道:

class open_properly(object):
    """
    This is a file writer that doesn't complain about unicode/string mismatches.
    It works for both python 2.7 and 3+.
    """
    def __init__(self, file_path):
        super(open_properly, self).__init__()
        self.file_path = file_path
    def __enter__(self):
        self.file_writer = open(self.file_path, 'w', encoding='utf8')
        self.file_writer.__enter__()
        return self
    def write(self, s):
        if not isinstance(s, string_types):
            raise ValueError("the text to print must be a valid string type")
        # make sure it's unicode (important for python 2)
        if isinstance(s, str):
            if hasattr(s, 'decode'):
                s = s.decode('utf-8')
        # write unicode to file
        self.file_writer.write(s)
    def __exit__(self, exc_type, exc_val, exc_tb):
        return self.file_writer.__exit__(exc_type, exc_val, exc_tb)