在 Python 2.x 中:
f = open('data.txt', 'rb')
As the docs http://docs.python.org/2/library/functions.html#open say:
默认情况下是使用文本模式,该模式可以在写入时将“\n”字符转换为特定于平台的表示形式,并在读取时将其转换回来。因此,当打开二进制文件时,您应该附加'b'
模式值以二进制模式打开文件,这将提高可移植性。 (附加'b'
即使在不以不同方式处理二进制文件和文本文件的系统上(它充当文档)也很有用。)
在 Python 3.x 中,有三种选择:
f1 = open('data.txt', 'rb')
这将使换行符保持不变,但也会返回bytes
代替str
,你必须明确地decode
自己进行 Unicode。 (当然,如果您想要 Unicode,2.x 版本也返回必须手动解码的字节,但在 2.x 中,这就是str
对象是;在 3.x 中str
是统一码。)
f2 = open('data.txt', 'r', newline='')
这将返回str
,并保留换行符不翻译。然而,与 2.x 等效版本不同的是,readline
和朋友会招待'\r\n'
作为换行符,而不是后跟换行符的常规字符。通常这并不重要,但如果确实如此,请记住这一点。
f3 = open('data.txt', 'rb', encoding=locale.getpreferredencoding(False))
这与 2.x 代码处理换行符的方式完全相同,并返回str
如果您只使用所有默认值,则使用相同的编码……但它在当前的 3.x 中不再有效。
从流中读取输入时,如果换行符为 None,则启用通用换行符模式。输入中的行可以以“\n”、“\r”或“\r\n”结尾,这些行在返回给调用者之前会被转换为“\n”。如果是 '',则启用通用换行模式,但行结尾会以未翻译的形式返回给调用者。
您需要指定显式编码的原因f3
以二进制模式打开文件意味着默认的“decode withlocale.getpreferredencoding(False)
” 到 “不解码,并返回原始数据bytes
代替str
”。再次,从the docs http://docs.python.org/3/library/functions.html#open:
在文本模式下,如果未指定编码,则使用的编码取决于平台:调用 locale.getpreferredencoding(False) 来获取当前区域设置编码。 (对于读取和写入原始字节,请使用二进制模式并保留未指定的编码。)
However:
'encoding' ...只能在文本模式下使用。
并且,至少从 3.3 开始,这是强制执行的;如果你尝试使用二进制模式,你会得到ValueError: binary mode doesn't take an encoding argument
.
那么,如果您想编写同时适用于 2.x 和 3.x 的代码,您会使用什么?如果您想经营bytes
, 明显地f
and f1are the same. But if you want to deal in
str, as appropriate for each version, the simplest answer is to write different code for each, probably
fand
f2`,分别。如果这种情况经常出现,请考虑编写任一包装函数:
if sys.version_info >= (3, 0):
def crlf_open(path, mode):
return open(path, mode, newline='')
else:
def crlf_open(path, mode):
return open(path, mode+'b')
编写多版本代码时要注意的另一件事是,如果您不编写区域设置感知代码,locale.getpreferredencoding(False)
在 3.x 中几乎总是返回一些合理的东西,但它通常只会返回'US-ASCII'
在 2.x 中。使用locale.getpreferredencoding(True)
从技术上讲是不正确的,但如果您不想考虑编码,则可能更可能是您真正想要的。 (尝试在 2.x 和 3.x 解释器中以两种方式调用它以了解原因,或者阅读文档。)
当然,如果您确实知道文件的编码,那总是比猜测要好。
无论哪种情况,'r'
意思是“只读”。如果不指定模式,则默认为'r'
,所以相当于默认的二进制模式是'rb'
.