要回答“为什么”,我们需要了解 Python 2.x 的内置file
类型file.encoding
,以及它们之间的关系。
内置file
对象处理原始字节——总是读取和写入原始字节。
该encoding
属性描述流中原始字节的编码。该属性可能存在也可能不存在,甚至可能不可靠(例如,我们PYTHONIOENCODING
在标准流的情况下设置不正确)。
对象执行任何自动转换的唯一时间file
是在将unicode
对象写入该流时。在这种情况下,它将使用file.encoding
if available 来执行转换。
在读取数据的情况下,文件对象不会进行任何转换,因为它返回的是原始字节。在这种情况下,该encoding
属性是用户手动执行转换的提示。
file.encoding
在您的情况下设置,因为您设置了PYTHONIOENCODING
变量并且sys.stdin
' 的encoding
属性已相应设置。要获得文本流,我们必须像您在示例代码中所做的那样手动包装它。
换一种方式考虑,假设我们没有单独的文本类型(如 Python 2.xunicode
或 Python 3 str
)。我们仍然可以使用原始字节处理文本,但要跟踪使用的编码。这就是它的file.encoding
用途(用于跟踪编码)。我们创建的阅读器包装器会自动为我们进行跟踪和转换。
当然,自动换行sys.stdin
会更好(这就是 Python 3.x 所做的),但是更改sys.stdin
Python 2.x 中的默认行为会破坏向后兼容性。
以下是sys.stdin
Python 2.x 和 3.x 中的比较:
# Python 2.7.4
>>> import sys
>>> type(sys.stdin)
<type 'file'>
>>> sys.stdin.encoding
'UTF-8'
>>> w = sys.stdin.readline()
## ... type stuff - enter
>>> type(w)
<type 'str'> # In Python 2.x str is just raw bytes
>>> import locale
>>> locale.getdefaultlocale()
('en_US', 'UTF-8')
自 Python 2.6 以来,io.TextIOWrapper
该类是标准库的一部分。此类具有encoding
用于将原始字节与 Unicode 相互转换的属性。
# Python 3.3.1
>>> import sys
>>> type(sys.stdin)
<class '_io.TextIOWrapper'>
>>> sys.stdin.encoding
'UTF-8'
>>> w = sys.stdin.readline()
## ... type stuff - enter
>>> type(w)
<class 'str'> # In Python 3.x str is Unicode
>>> import locale
>>> locale.getdefaultlocale()
('en_US', 'UTF-8')
该buffer
属性提供对原始字节流支持的访问stdin
;这通常是一个BufferedReader
. 请注意,它没有属性encoding
。
# Python 3.3.1 again
>>> type(sys.stdin.buffer)
<class '_io.BufferedReader'>
>>> w = sys.stdin.buffer.readline()
## ... type stuff - enter
>>> type(w)
<class 'bytes'> # bytes is (kind of) equivalent to Python 2 str
>>> sys.stdin.buffer.encoding
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: '_io.BufferedReader' object has no attribute 'encoding'
在 Python 3 中,属性的存在与否encoding
与所使用的流类型一致。