帮助我理解为什么Unicode只在Python中有效

end*_*ith 11 python unicode ubuntu ipython windows-7

这是一个小程序:

#!/usr/bin/env python
# -*- encoding: utf-8 -*-

print('abcd k? ? °C ?Hz µF ü ? ?')  
print(u'abcd k? ? °C ?Hz µF ü ? ?')
Run Code Online (Sandbox Code Playgroud)

在Ubuntu,Gnome终端上,IPython做了我所期望的:

In [6]: run Unicodetest.py
abcd k? ? °C ?Hz µF ü ? ?
abcd k? ? °C ?Hz µF ü ? ?
Run Code Online (Sandbox Code Playgroud)

如果我在trypython.org上输入命令,我会得到相同的输出.

另一方面,codepad.org会为第二个命令生成错误:

abcd k? ? °C ?Hz µF ü ? ?
Traceback (most recent call last):
  Line 6, in <module>
    print(u'abcd k? ? °C ?Hz µF ü ? ?')
UnicodeEncodeError: 'ascii' codec can't encode character u'\u03a9' in position 6: ordinal not in range(128)
Run Code Online (Sandbox Code Playgroud)

相反,Windows上的IDLE会破坏第一个命令的输出,但不会抱怨第二个命令:

>>>
abcd kΩ ☠ °C √Hz µF ü ☃ ♥
abcd k? ? °C ?Hz µF ü ? ?
Run Code Online (Sandbox Code Playgroud)

在Windows命令提示符下或通过Python(x,y)的Console2版本的IPython都会破坏第一个输出并抱怨第二个输出:

In [9]: run Unicodetest.py
abcd k?? ?ÿá ??C ?êÜHz ??F ?? ?ÿâ ?ÖÑ
ERROR: An unexpected error occurred while tokenizing input
The following traceback may be corrupted or invalid
The error message is: ('EOF in multi-line statement', (15, 0))

---------------------------------------------------------------------------
UnicodeEncodeError                        Traceback (most recent call last)

Desktop\Unicodetest.py in <module>()
      4 print('abcd k?? ?ÿá ??C ?êÜHz ??F ?? ?ÿâ ?ÖÑ')
      5
----> 6 print(u'abcd k?? ?ÿá ??C ?êÜHz ??F ?? ?ÿâ ?ÖÑ')
      7
      8

C:\Python27\lib\encodings\cp437.pyc in encode(self, input, errors)
     10
     11     def encode(self,input,errors='strict'):
---> 12         return codecs.charmap_encode(input,errors,encoding_map)
     13
     14     def decode(self,input,errors='strict'):

UnicodeEncodeError: 'charmap' codec can't encode character u'\u2620' in position 8: character maps to <undefined>
WARNING: Failure executing file: <Unicodetest.py>
Run Code Online (Sandbox Code Playgroud)

Python中的IPython(x,y)的Spyder也是如此,但不同:

In [8]: run Unicodetest.py
abcd kΩ ☠ °C √Hz µF ü ☃ ♥
------------------------------------------------------------
Traceback (most recent call last):
  File "Unicodetest.py", line 6, in <module>
    print(u'abcd kΩ ☠ °C √Hz µF ü ☃ ♥')
  File "C:\Python26\lib\encodings\cp1252.py", line 12, in encode
    return codecs.charmap_encode(input,errors,encoding_table)
UnicodeEncodeError: 'charmap' codec can't encode character u'\u03a9' in position 6: character maps to <undefined>

WARNING: Failure executing file: <Unicodetest.py>
Run Code Online (Sandbox Code Playgroud)

(在sitecustomize.py中,Spyder SPYDER_ENCODING根据语言环境模块的编码设置了自己的编码,cp1252适用于Windows 7.)

是什么赋予了?我的命令之一是错的吗?为什么一个平台在某些平台上运行而另一个平台在其他平台上运行?如何在不崩溃或搞砸的情况下始终如一地打印Unicode字符?

是否有一个Windows的备用终端,其行为类似于Ubuntu中的终端?似乎TCC-LE,Console2,Git Bash,PyCmd等都只是cmd.exe的包装而不是替换.有没有办法在IDLE使用的接口内运行IPython?

dan*_*n04 11

Python(以及大多数其他语言)中的I/O基于字节.当您将一个字节字符串(str在2.x中,bytes在3.x中)写入文件时,字节只是按原样写入.将Unicode字符串(unicode在2.x中,str在3.x中)写入文件时,需要将数据编码为字节序列.

有关这种区别的进一步解释,请参阅关于字符串Dive into Python 3章节.

print('abcd k? ? °C ?Hz µF ü ? ?')
Run Code Online (Sandbox Code Playgroud)

这里,字符串是一个字节串.因为源文件的编码是UTF-8,所以字节是

'abcd k\xce\xa9 \xe2\x98\xa0 \xc2\xb0C \xe2\x88\x9aHz \xc2\xb5F \xc3\xbc \xe2\x98\x83 \xe2\x99\xa5'
Run Code Online (Sandbox Code Playgroud)

print语句按原样将这些字节写入控制台.但是Windows控制台将字节字符串解释为在"OEM"代码页中编码,在美国是437.所以你在屏幕上看到的字符串是

abcd k?? ?ÿá ??C ?êÜHz ??F ?? ?ÿâ ?ÖÑ
Run Code Online (Sandbox Code Playgroud)

在您的Ubuntu系统上,这不会导致问题,因为默认控制台编码是UTF-8,因此您没有源文件编码和控制台编码之间的差异.

print(u'abcd k? ? °C ?Hz µF ü ? ?')
Run Code Online (Sandbox Code Playgroud)

打印Unicode字符串时,必须将字符串编码为字节.但它只有在您拥有支持这些字符的编码时才有效.而你没有.

  • 默认的IBM437编码缺少字符 ???
  • Spyder使用的windows-1252编码缺少字符?????.

因此,在这两种情况下,您都会遇到尝试打印字符串的UnicodeEncodeError.

是什么赋予了?

Windows和Linux采用了截然不同的方法来支持Unicode.

最初,它们的工作方式大致相同:每个语言环境都有自己的语言特定char编码(Windows中的"ANSI代码页").西方语言使用ISO-8859-1或windows-1252,俄语使用KOI8-R或windows-1251等.

当Windows NT添加对Unicode的支持时(在假设Unicode将使用16位字符的早期阶段),它通过创建用于wchar_t代替的API的并行版本来实现char.例如,MessageBox函数分为两个函数:

int MessageBoxA(HWND hWnd, const char* lpText, const char* lpCaption, unsigned int uType);
int MessageBoxW(HWND hWnd, const wchar_t* lpText, const wchar_t* lpCaption, unsigned int uType);
Run Code Online (Sandbox Code Playgroud)

"W"功能是"真实的"功能."A"函数用于向后兼容基于DOS的Windows,并且主要只是将其字符串参数转换为UTF-16,然后调用相应的"W"函数.

在Unix世界(特别是Plan 9)中,编写一个全新版本的POSIX API被认为是不切实际的,因此以不同的方式处理Unicode支持.CJK语言环境中对多字节编码的现有支持用于实现现在称为UTF-8的新编码.

在编写支持Unicode的跨平台代码时,类Unix系统上的UTF-8和Windows上的UTF-16的偏好是一个巨大的痛苦.Python试图将其隐藏在程序员之外,但打印到控制台是Joel的"漏洞抽象"之一.