【问题标题】:Make python3 default to latin-1 for a script?使脚本的python3默认为latin-1?
【发布时间】:2015-11-24 14:53:03
【问题描述】:

TL;DR:我可以让 Python 3 使用除 unicode 之外的任何东西作为所有东西的默认编码吗?

我有一些用 Python 3 编写的脚本。在我自己的文件上运行时,它们运行良好,因为这些文件以 utf-8 编码并且通常只使用与 ASCII 兼容的子集。

现在我尝试在几十年前的源文件上使用相同的脚本,我得到了左右的 unicode 异常。完全有可能,这些文件是由编辑器在一年中使用不同的编码进行编辑的,因此每个文件的编码可能不同,甚至定义不明确。

如果我在 Python 2 中编写脚本,假定采用固定宽度编码,那么一切都会正常工作。无论如何,使用非 ascii 字符的部分仅在 cmets 中。

在 Python3 中,当编码未知且可能定义不明确时,干净的解决方案是仅对字节数组数据进行操作,但缺少 .format 函数以及需要在任何地方区分字节和 str 文字都是句法噩梦,而且太耗时,无法在我的脚本中修复,不值得。

是否可以将 sys.stdin、sys.stderr 和所有文件 opened 的假定默认编码更改为固定宽度编码?这样做可以让我的脚本以“字节输入,字节输出”的方式工作,这真的更适合我对 shell 脚本的使用(并且最终会更稳定)。

理想情况下,解决方案应该基于每个脚本并允许忽略环境变量。

基于https://stackoverflow.com/a/12823030/2075630,我能想到的最好的是

sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="latin-1")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="latin-1")
sys.stdin  = io.TextIOWrapper(sys.stdin.buffer,  encoding="latin-1")

# To avoid changing individual `open` calls: 
open_ = open
def open(*a,**b):
    b["encoding"] = "latin-1"
    return open_(*a,**b)

但这会导致 STDOUT 和 STDERR 流被大量缓冲,这对于 shell 脚本来说是不可取的。

【问题讨论】:

  • 为什么你认为这会导致大量缓冲?您没有添加任何缓冲,而是删除了文本 I/O 包装器,将其替换为另一个包装器。这不会改变缓冲行为。
  • 所以我会想到。但在添加行之前,脚本会在运行时产生输出。添加行后,输出将在处理完成后全部输出。显然,新的 TextIOWrapper 增加了缓冲。
  • TextIOWrapper() 的唯一缓冲选项是 line_buffering 参数。将其设置为 True 以在写入换行符时向底层缓冲区发送隐式 flush()
  • 谢谢,该参数部分解决了这个问题(因为不仅"\n" 而且"\r" 似乎会导致刷新,这是某些状态消息所必需的)。您可以将该更改转换为我可以接受的答案(通过此更改,它可以满足我的要求)。
  • 您始终可以在print() 语句中添加flush=True 参数。

标签: python python-3.x encoding


【解决方案1】:

Python 2 不假设 any 编码。它基本上对字节进行操作。以二进制模式读取文件并处理 bytes 以返回该模式。

您可以通过访问.buffer 属性将 STDIO 流视为二进制:

bytes_from_stdin = sys.stdin.buffer.read()
sys.stdout.buffer.write(bytes_to_stdout)

在文件模式中添加'b',以二进制模式打开文件。

通常,为 STDIO 编码/解码选择的编解码器基于运行脚本的终端的当前语言环境。要切换编解码器,您可以在终端中切换语言环境,或者通过设置 PYTHONIOENCODING 环境变量为 Python 设置一个:

PYTHONIOENCODING=latin1 ./yourscript.py

文本文件应始终使用显式编解码器打开;不要依赖系统默认值。不过,我不确定修补 open() 是不是最好的方法。

TextIOWrapper() 的缓冲问题可以通过启用行缓冲来解决;如果您设置了line_buffering=True,则每次将\n 换行符写入包装器时都会执行一个隐式buffer.flush() 调用:

sys.stdout = io.TextIOWrapper(
    sys.stdout.buffer, encoding="latin-1", line_buffering=True)

【讨论】:

  • 遗憾的是,使用基于字节的 API 会导致冗长的代码(直到 python 3.4 至少没有 bytes.format)和大量重构(用字节文字替换字符串文字,但要小心不要替换字节无效的文字)。这可能是一个干净的解决方案,但对于一组 shell 脚本来说是不可行的。使用 PYTHONIOENCODING 是可行的,但如果设置为全局值,则需要包装脚本或引入模糊的机器相关行为。至于修补open(),该解决方案旨在避免重构受影响的脚本。
  • @kdb:你的意思是将其标记为已接受,然后再次不接受?有什么我可以帮忙的吗?
猜你喜欢
  • 2019-06-06
  • 2020-06-15
  • 2016-06-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多