Python 2.x 中的字符串使用哪种编码?

Which encoding is used for strings in Python 2.x?

python 2.x 中用于编码字符串的默认编码是什么?我读过有两种可能的方法来声明一个字符串。

string = 'this is a string'
unicode_string = u'this is a unicode string'

第二个字符串是 Unicode。 第一个字符串的编码是什么?

字面上的答案是它们不一定代表任何特定的编码。在 Python 2 中,字符串只是一个字节数组,与 Python 中的 bytes 类型完全一样 3. 对于字符串 s 你可以调用 s.decode()获取 Unicode 字符串,但正是出于这个原因,您 必须 * 手动传递编码。您可以使用 string 来保存 ASCII 字节或来自 Windows code page 850(ASCII 的超集)的字符,或 UTF8 字节,甚至 UTF16 字节。最后一种情况很有趣,因为即使该字符串中的字符在 ASCII 范围内,字节也不匹配 ASCII 编码版本(它们将与空字符交替出现)。 string 类型甚至适用于不对应于任何编码字符串的某些二进制格式的字节,例如图像文件的字节数。

一个更实际的答案是通常假定为 ASCII。例如,文字字符串 "xyz" 将给出一个三字节字符串,其字节对应于这些字符的 ASCII 编码。

这种歧义是 Python 3 中围绕字符串的行为和约定发生变化的原因。

* 如 CristiFati 的回答中所述,可以省略 decodeencoding= 参数,在这种情况下将假定为 ASCII。我的错。

正如@ArthurTacca 在他的回答中解释的那样,一个字符串 ("this is a string") 只是一个字节数组 (0x74 0x68 0x69 0x73 0x20 0x69 0x73 0x20 0x61 0x20 0x73 0x74 0x72 0x69 0x6e 0x67),它的编码在解码上下文之外没有任何意义(当字节是已解释)。

查看 [Python 2.Docs]: sys.getdefaultencoding()

>>> sys.version
'2.7.10 (default, Mar  8 2016, 15:02:46) [MSC v.1600 64 bit (AMD64)]'
>>> sys.getdefaultencoding()
'ascii'
>>> "\xff".decode()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xff in position 0: ordinal not in range(128)

根据(简明扼要地叙述其 Py2 部分,以尽量减少重复):

Python2中实际上有多个独立的"default"字符串编码,被其功能的不同部分使用。

  • 解析代码和字符串文字:

    • str 来自文字——将包含文件中的原始字节,不进行转码
    • unicode 来自文字——文件中的字节与文件的 "source encoding" 进行 decode 运算,默认为 ascii
    • with unicode_literals future, 文件中的所有文字都被视为 Unicode 文字
  • Transcoding/type转换:

    • str<->unicode 类型转换和 encode/decode w/o 参数用 sys.getdefaultencoding() 完成
      • 几乎总是 ascii,所以任何国家字符都会导致 UnicodeError
    • str 只能 decodeunicode -- encode。否则尝试将涉及隐式类型转换(具有上述结果)
  • I/O,包括printing:

    • unicode -- 如果设置了 encode<file>.encoding,否则隐式转换为 str(具有上述结果)
    • str -- 原始字节写入流,不进行转码。对于国家字符,终端将根据其区域设置显示不同的字形。

第一个字符串没有编码。它是原始字节。向自己证明这一点的一种令人信服的方法是使用编码声明将用于解码源代码的编码更改为其他编码。这样你就可以明显地分辨出 ASCII 和 bytes 之间的区别。

将其保存到 .py 文件并执行:

# coding: rot13

s0 =  "this is a string"
s1 = o"this is a string"
s2 = h"guvf vf n fgevat"

nffreg s0 == s1 == s2
cevag s0
cevag s1
cevag s2

如果您想了解原因,此源已编码为 simple letter substitution cipher. Letters in a-z A-Z are "rotated" by 13 places, other characters are unchanged. Since there are 26 letters in the alphabet, rotating twice is an identity transform. Note that the coding declaration itself is not rotated, see PEP 263

  • nffreg是断言语句,说这三个字符串都比较相等。
  • cevag 是打印语句。
  • s2 是一个带有旋转 u 前缀的 unicode 字符串。另外两个是字节串。

现在,让我们通过引入 unicode 文字 __future__ 导入来更改第一个字符串的处理。请注意,此 future statement 本身必须旋转,否则会出现语法错误。这改变了 tokenizer/compiler 组合处理第一个对象的方式,这将变得很明显:

# coding: rot13
sebz __shgher__ vzcbeg havpbqr_yvgrenyf

s0 =  "guvf vf n fgevat"
s1 = o"this is a string"
s2 = h"guvf vf n fgevat"

nffreg s0 == s1 == s2
cevag s0
cevag s1
cevag s2

我们需要将文本从 this is a string 更改为 guvf vf n fgevat 以使断言语句保持有效。 这表明第一个字符串没有编码。