133
|
Michael Ekoka · 技术社区 · 15 年前 |
![]() |
1
98
多亏了各种各样的回复,我想我们可以编一个解释。 通过尝试打印unicode字符串u'\xe9',python隐式地尝试使用当前存储在sys.stdout.encoding中的编码方案对该字符串进行编码。python实际上是从它所启动的环境中获取这个设置的。如果它无法从环境中找到正确的编码,那么只有这样它才会恢复到 违约 ,ASCII码。 例如,我使用一个bash shell,它的编码默认为utf-8。如果我从中启动python,它会选择并使用该设置:
让我们先退出python shell,用一些伪编码设置bash的环境:
然后再次启动python shell并验证它是否确实恢复为默认的ASCII编码。
答对了! 如果您现在尝试在ASCII之外输出一些Unicode字符,应该会收到一条很好的错误消息。
让我们退出python并丢弃bash shell。 现在我们将观察在Python输出字符串之后会发生什么。为此,我们首先在图形终端内启动bash shell(我使用gnome终端),然后将终端设置为使用iso-8859-1(即拉丁语-1)解码输出(图形终端通常可以选择 设置字符编码 在其中一个下拉菜单中)。注意,这不会改变 壳牌环境 编码,它只改变了 终端 它本身将解码给定的输出,有点像Web浏览器。因此,您可以独立于shell环境更改终端的编码。然后让我们从shell启动python,并验证sys.stdout.encoding是否设置为shell环境的编码(对于我来说是utf-8):
(1)python按原样输出二进制字符串,终端接收该字符串,并尝试将其值与拉丁-1字符映射匹配。在拉丁语-1中,0xe9或233生成字符“_”),因此终端显示的就是这个字符。 (2)python试图 含蓄地 使用sys.stdout.encoding中当前设置的任何方案对unicode字符串进行编码,在本例中是“utf-8”。在UTF-8编码之后,生成的二进制字符串是'\xc3\xa9'(见后面的解释)。终端接收流,并尝试使用拉丁语-1解码0xc3a9,但拉丁语-1从0变为255,因此一次只能解码流1字节。0xc3a9是2个字节长,因此拉丁语1解码器将其解释为0xc3(195)和0xa9(169),并生成2个字符:和。 (3)python使用拉丁-1方案对unicode码位u'\xe9'(233)进行编码。结果发现,拉丁-1代码点的范围是0-255,并且在该范围内指向与Unicode完全相同的字符。因此,当用拉丁文-1编码时,该范围内的Unicode代码点将产生相同的值。因此,用拉丁语1编码的u''xe9'(233)也将生成二进制字符串''xe9'。终端接收该值并尝试在拉丁-1字符映射上匹配它。就像案例(1),它产生“_”,这就是所显示的。 现在,让我们从下拉菜单中将终端的编码设置更改为UTF-8(就像更改Web浏览器的编码设置一样)。无需停止python或重新启动shell。终端的编码现在与python的匹配。让我们再次尝试打印:
(4)python输出 二元的 字符串保持不变。终端尝试用UTF-8解码该流。但是UTF-8不理解值0xE9(见后面的解释),因此无法将其转换为Unicode码位。未找到代码点,未打印任何字符。 (5)python试图 含蓄地 使用sys.stdout.encoding中的任何内容对unicode字符串进行编码。仍然是“UTF-8”。生成的二进制字符串是'\xc3\xa9'。终端接收流并尝试使用utf-8解码0xc3a9。它返回代码值0xE9(233),该值在Unicode字符映射上指向符号“”。终端显示“”。 (6)python用拉丁文-1对unicode字符串进行编码,生成一个具有相同值'\xe9'的二进制字符串。同样,对于终端来说,这与案例(4)基本相同。 结论: -python输出非unicode字符串作为原始数据,而不考虑其默认编码。如果终端当前的编码与数据匹配,那么它恰好会显示它们。 -python使用sys.stdout.encoding中指定的方案对Unicode字符串进行编码后输出这些字符串。 -python从shell的环境中获取该设置。 -终端根据自己的编码设置显示输出。 -终端的编码与外壳无关。 有关Unicode、UTF-8和Latin-1的详细信息: Unicode基本上是一个字符表,其中一些键(代码点)按惯例分配给某些符号。例如,根据惯例,确定键0xE9(233)是指向符号“”的值。ASCII和Unicode使用的代码点从0到127,与拉丁语1和Unicode从0到255相同。也就是说,在ASCII、拉丁文-1和Unicode中,0x41指向“A”,在拉丁文-1和Unicode中,0xC8指向“”,在拉丁文-1和Unicode中,0xE9指向“”。 使用电子设备时,Unicode码位需要一种有效的电子表示方法。这就是编码方案的意义所在。存在各种Unicode编码方案(utf7、utf-8、utf-16、utf-32)。最直观和直接的编码方法是简单地使用Unicode映射中的代码点值作为其电子形式的值,但Unicode目前拥有超过一百万个代码点,这意味着其中一些代码点需要3个字节来表示。为了有效地处理文本,1到1的映射是非常不切实际的,因为它需要将所有代码点存储在完全相同的空间中,每个字符至少有3个字节,而不管它们的实际需要如何。 大多数编码方案在空间需求方面都存在缺陷,最经济的方案不包括所有Unicode码位,例如,ASCII仅覆盖前128个码位,而Latin-1则覆盖前256个码位。其他试图更全面的方法也会造成浪费,因为它们需要比需要更多的字节,甚至对于普通的“廉价”字符也是如此。例如,UTF-16每个字符至少使用2个字节,包括那些在ASCII范围内的字节(“b”是65,仍然需要2个字节的UTF-16存储)。UTF-32更加浪费,因为它将所有字符存储在4个字节中。 UTF-8恰好巧妙地解决了这一难题,它的方案能够用可变字节空间存储代码点。作为其编码策略的一部分,UTF-8使用标志位来标识(可能是解码器)它们的空间需求和边界。 ASCII范围(0-127)中Unicode码位的UTF-8编码:
例如,“b”的Unicode码位是二进制的“0x42”或“0100 0010”(正如我们所说,它在ASCII中是相同的)。以UTF-8编码后,它变为:
127(非ASCII)以上的Unicode码位的UTF-8编码:
例如,‘unicode码位是0xe9(233)。
当UTF-8编码该值时,它确定该值大于127小于2048,因此应以2字节编码:
在UTF-8编码之后的0xE9 Unicode代码点变为0xC3A9。这正是终端接收它的方式。如果您的终端设置为使用Latin-1(非Unicode传统编码之一)解码字符串,那么您将看到,因为正好相反,Latin-1中的0xc3指向,而0xa9指向。 |
![]() |
2
25
当Unicode字符打印到stdout时,
请注意,python 3.6或更高版本忽略了Windows上的编码,并使用unicode API将unicode写入终端。没有unicodeencodeerror警告,如果字体支持,则显示正确的字符。即使字体 不 支持它,字符仍然可以从终端剪切n-粘贴到支持字体的应用程序中,它将是正确的。升级! |
![]() |
3
8
python repl尝试从您的环境中获取要使用的编码。如果它发现什么东西是正常的,那么一切都是正常的。当它不知道发生了什么事情的时候,它就会崩溃。
|
![]() |
4
4
你
有
通过输入显式Unicode字符串指定编码。比较不使用的结果
如果是
|
![]() |
5
0
按 Python default/implicit string encodings and conversions :
|
![]() |
6
-1
它对我有用:
|