对于脚本,将python3默认为latin-1?

时间:2015-11-24 14:53:04

标签: python python-3.x encoding

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

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

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

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

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

是否可以将没有显式编码的sys.stdin,sys.stderr和所有文件open的假定默认编码更改为固定宽度编码?这样做将允许我的脚本作为“字节输入,字节输出”工作,这将更适合我对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脚本来说是不可取的。

1 个答案:

答案 0 :(得分:0)

Python 2不假设任何编码。它基本上按字节操作。以二进制模式读取文件并处理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()的缓冲问题可以通过启用线路缓冲来解决;如果设置buffer.flush(),则每次向包装器写入\n换行符时都会执行隐式line_buffering=True调用:

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