Python何时,何地以及如何隐式地将编码应用于字符串或隐式转码(转换)?
这些"默认"(即隐含)编码是什么?
例如,什么是编码:
-
字符串文字?
s = "Byte string with national characters" us = u"Unicode string with national characters"
-
当类型转换为 Unicode 和从 Unicode 转换时字节字符串?
data = unicode(random_byte_string)
-
当字节和 Unicode 字符串写入文件或终端时?
print(open("The full text of War and Peace.txt").read())
这里涉及 Python 功能的多个部分:读取源代码和解析字符串文字、转码和打印。每个都有自己的约定。
简短的回答:
- 出于代码解析的目的:
str
(Py2) -- 不适用,从文件中获取原始字节- -- "源编码",默认值为
ascii
(Py2) 和utf-8
(Py3) bytes
(Py3) -- 无,文字中禁止使用非 ASCII 字符
unicode
(Py2)/str
(Py3) - 出于转码的目的:
- 两者 (Py2) --
sys.getdefaultencoding()
(ascii
几乎总是)- 有一些隐式转换通常会导致
UnicodeDecodeError
/UnicodeEncodeError
- 有一些隐式转换通常会导致
- 两者 (Py3) -- none,必须在转换时显式指定编码
- 两者 (Py2) --
- 对于 I/O:
unicode
(Py2) -- 如果设置<file>.encoding
,否则sys.getdefaultencoding()
str
(Py2) -- 不适用,写入原始字节str
(Py3) --<file>.encoding
,始终设置并默认为locale.getpreferredencoding()
bytes
(Py3) -- 无,print
ing 产生它的repr()
首先,澄清一些术语,以便您正确理解其余部分。解码是从字节到字符(Unicode或其他方式)的转换,编码(作为一个过程)则相反。查看每个软件开发人员绝对、肯定必须了解 Unicode 和字符集的绝对最低限度(没有任何借口!) – Joel on Software 以获得区别。
现在。。。
读取源和分析字符串文本
在源文件的开头,您可以指定文件的"源编码"(其确切效果将在后面介绍)。如果未指定,则 Python 2 的默认值为ascii
,Python 3 的默认值为utf-8
。UTF-8 BOM 与utf-8
编码声明具有相同的效果。
蟒蛇 2
Python 2 将源代码读取为原始字节。它只使用"源编码"在看到 Unicode 文本时解析它。(它比引擎盖下更复杂,但这是净效应。
> type t.py
# Encoding: cp1251
s = "абвгд"
us = u"абвгд"
print repr(s), repr(us)
> py -2 t.py
'xe0xe1xe2xe3xe4' u'u0430u0431u0432u0433u0434'
<change encoding declaration in the file to cp866, do not change the contents>
> py -2 t.py
'xe0xe1xe2xe3xe4' u'u0440u0441u0442u0443u0444'
<transcode the file to utf-8, update declaration or replace with BOM>
> py -2 t.py
'xd0xb0xd0xb1xd0xb2xd0xb3xd0xb4' u'u0430u0431u0432u0433u0434'
因此,常规字符串将包含文件中的确切字节。Unicode 字符串将包含使用"源编码"解码文件字节的结果。
如果解码失败,您将获得SyntaxError
。如果未指定编码时文件中存在非 ASCII 字符,则相同。最后,如果使用unicode_literals
将来,则任何常规字符串文本(仅在该文件中)在解析时都被视为 Unicode 文本,这意味着什么。
蟒蛇 3
Python 3 使用"源编码"将整个源文件解码为一系列 Unicode 字符。任何解析都是在那之后完成的。(特别是,这使得标识符中包含 Unicode 成为可能。由于所有字符串文字现在都是 Unicode,因此不需要额外的转码。在字节文本中,禁止使用非 ASCII 字符(此类字节必须使用转义序列指定),从而完全避免了此问题。
转码
根据开头的说明:
str
(Py2)/bytes
(Py3) --字节=> 只能decode
d(直接,即;详情如下)unicode
(Py2)/str
(Py3) --字符=> 只能encode
d
蟒蛇 2
在这两种情况下,如果未指定编码,则使用sys.getdefaultencoding()
。它是ascii
(除非您在site.py
中取消注释代码块,或者执行其他一些会导致灾难的黑客攻击)。因此,出于转码的目的,sys.getdefaultencoding()
是"字符串的默认编码"。
现在,这里有一个警告:
转换
str<->unicode
时隐式地完成decode()
和encode()
- 使用默认编码:- 字符串格式(堆栈溢出上
UnicodeDecodeError
/UnicodeEncodeError
问题的三分之一是关于这个的) - 尝试
encode()
str
或decode()
unicode
时(堆栈溢出问题的后三分之一)
- 字符串格式(堆栈溢出上
蟒蛇 3
根本没有"默认编码":现在禁止在str
和bytes
之间进行隐式转换。
bytes
只能decode
d和str
-encode
d,encoding
参数是强制性的。- 转换
bytes->str
(包括隐式转换)会产生其repr()
(仅对调试打印有用),完全避免编码问题 - 禁止转换
str->bytes
印刷
这个问题与变量的值无关,但与print
时在屏幕上看到的内容有关 - 以及print
时是否会得到UnicodeEncodeError
。
蟒蛇 2
- 如果设置了
unicode
,则encode
d 与<file>.encoding
d;否则,它将按照上述方式隐式转换为str
。(UnicodeEncodeError
SO问题的最后三分之一属于这里。- 对于标准流,流的编码在启动时从各种特定于环境的源猜测,并且可以使用
PYTHONIOENCODING
环境变量覆盖。
- 对于标准流,流的编码在启动时从各种特定于环境的源猜测,并且可以使用
str
的字节按原样发送到操作系统流。您将在屏幕上看到的特定字形取决于终端的编码设置(如果它是 UTF-8 之类的东西,如果您打印的字节序列无效的 UTF-8,您可能什么也看不到)。
蟒蛇 3
这些更改是:
- 现在
file
用文本与二进制打开,mode
本身接受str
或bytes
,相应地,并完全拒绝处理错误的类型。文本模式文件始终设置encoding
,locale.getpreferredencoding(False)
默认设置。 - 文本流的
print
仍然隐式将所有内容转换为str
,在bytes
的情况下,按照上述方式打印其repr()
,完全回避编码问题
隐式编码作为内部格式来存储字符串/数组:你不应该关心编码。事实上,Python以Python内部的方式解码字符。它基本上是透明的。只需想象它是 Unicode 文本或字节序列,以抽象的方式。
Python 3.x 中的内部编码根据"较大"字符而有所不同。它可以是 UTF-8/ASCII(用于 ASCII 字符串)、UTF-16 或 UTF-32。当你使用字符串时,就像你有一个Unicode字符串(非常抽象,而不是真正的编码)。如果您不使用 C 编程或使用某些特殊函数(内存视图),您将永远无法看到内部编码。
字节只是实际内存的视图。Python的解释是unsigned char
.但同样,通常你应该只考虑它的顺序是什么,而不是内部编码。
Python 2将字节和字符串作为无符号字符,Unicode 作为 UCS-2(因此 65535 以上的代码点在 Python 2 中将使用两个字符 (UCS-2) 进行编码,在 Python 3 中仅使用一个字符 (UTF-32) 进行编码)。