帮我理解为什么Unicode在Python中只能有时候起作用
帮我理解为什么Unicode在Python中只能有时候起作用
这是一个小程序:\n
#!/usr/bin/env python # -*- encoding: utf-8 -*- print('abcd kΩ °C √Hz µF ü ') print(u'abcd kΩ °C √Hz µF ü ')
\n在Ubuntu的Gnome终端上,IPython的表现符合我的期望:\n
In [6]: run Unicodetest.py abcd kΩ °C √Hz µF ü abcd kΩ °C √Hz µF ü
\n如果我在trypython.org上输入这些命令,我会得到相同的输出。\n而codepad.org则对第二个命令产生错误:\n
abcd kΩ °C √Hz µF ü Traceback (most recent call last): Line 6, inprint(u'abcd kΩ °C √Hz µF ü ') UnicodeEncodeError: 'ascii' codec can't encode character u'\u03a9' in position 6: ordinal not in range(128)
\n相反,Windows上的IDLE会破坏第一个命令的输出,但对第二个命令不会报错:\n
>>> abcd kΠ☠°C √Hz µF ü ☃ ⥠abcd kΩ °C √Hz µF ü
\nWindows命令提示符中的IPython或Python(x,y)的Console2版本会破坏第一个输出并报错第二个:\n
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() 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 WARNING: Failure executing file:
\nPython(x,y)的Spyder中的IPython也会产生相同的错误,但方式不同:\n
In [8]: run Unicodetest.py abcd kΠ☠°C √Hz µF ü ☃ ⥠------------------------------------------------------------ Traceback (most recent call last): File "Unicodetest.py", line 6, inprint(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 WARNING: Failure executing file:
\n(在sitecustomize.py中,Spyder根据locale模块的编码设置了自己的SPYDER_ENCODING,Windows 7上为cp1252。)\n这是怎么回事?我哪个命令写错了吗?为什么一个在某些平台上工作而另一个在其他平台上工作?如何稳定地打印Unicode字符而不崩溃或损坏输出?\n有没有一个在Windows上像Ubuntu上的那个终端一样的替代品?看起来TCC-LE、Console2、Git Bash、PyCmd等都只是cmd.exe的封装而不是替代品。有没有办法在IDLE使用的界面中运行IPython?
帮助我理解为什么Python只在某些情况下使用Unicode能正常工作
问题出现的原因是程序期望和输出UTF-8字符,但控制台和各种网络上的Python运行器使用其他代码页。如果不进行修改,没有办法在所有编码中编写可正常工作的特殊字符。然而,如果选择在所有地方都使用UTF-8,应该是安全的。
我认为Windows中的任何终端都可以使用,所以不要因为这个问题而更换默认终端(cmd.exe)。相反,将终端的编码设置为UTF-8,以便与Python脚本的编码匹配。
不幸的是,我从来没有找到一种将代码页设置为UTF-8的默认方式,所以每次打开新的命令提示符时都必须进行设置。但是它通过一个简单的命令完成,所以还算不错...你可以通过切换代码页来改变编码:
>chcp 65001
当前代码页已更改为65001
请注意,你必须使用标准字体之一才能使其正常工作。大多数网络上的来源似乎建议使用Lucida Console。
现在,我尝试的每个命令都失败并显示"LookupError: unknown encoding: cp65001",这是由于在"C:\Python27\lib\site-packages\IPython\iplib.pyc"中的"line = raw_input_original(prompt).decode(self.stdin_encoding)"引起的。
不幸的是,chcp 65001存在许多问题。Microsoft C运行时和默认的Windows控制台都设计为与特定区域设置的代码页配合使用;当其他人都在使用UTF-8作为一切的编码时,这是一种真正的遗憾。
Python中Unicode只在某些情况下工作的原因是由于以下两个可能的原因:
1. 通过print
对Unicode进行编码。不能直接输出原始的Unicode,所以print
需要确定如何将其转换为控制台所期望的字节流(它使用sys.stdout.encoding
),这就涉及到了:
2. 控制台的支持。Python无法控制你的终端,因此如果它输出的是UTF-8,而你的终端期望的是其他编码,你将得到乱码输出。
为了解决这个问题,可以尝试以下方法:
1. 确保在使用print
输出Unicode之前,先将其正确地编码为控制台所期望的字节流。可以使用unicode.encode
方法来实现这一点,例如:print(unicode.encode('utf-8'))
。
2. 确保终端支持Unicode编码,可以通过设置终端的编码方式为UTF-8来解决这个问题。具体的设置方法取决于你使用的终端类型和操作系统。
通过解决上述两个问题,就可以确保Python中的Unicode在所有情况下正常工作。
帮助我理解为什么Unicode在Python中有时候只能部分工作的原因。
在Python中,I/O(以及大多数其他编程语言)是基于字节的。当你将一个字节字符串(2.x版本中的str,3.x版本中的bytes)写入文件时,字节会按原样写入。当你将一个Unicode字符串(2.x版本中的unicode,3.x版本中的str)写入文件时,数据需要被编码为一个字节序列。
进一步解释这个区别的说明可以参见“Dive into Python 3”中关于字符串的章节。
print('abcd kΩ °C √Hz µF ü ')
在这里,字符串是一个字节字符串。因为你的源文件的编码是UTF-8,字节是:
'abcd k\xce\xa9 \xe2\x98\xa0 \xc2\xb0C \xe2\x88\x9aHz \xc2\xb5F \xc3\xbc \xe2\x98\x83 \xe2\x99\xa5'
`print`语句会按原样将这些字节写入控制台。但是Windows控制台将字节字符串解释为使用"OEM"代码页编码的。在美国,这个代码页是437。所以你在屏幕上实际看到的字符串是:
'abcd kΩ ☠ °C √Hz µF ü ☃ ♥'
在你的Ubuntu系统上,这不会造成问题,因为默认的控制台编码是UTF-8,所以你不会有源文件编码和控制台编码之间的差异。
print(u'abcd kΩ °C √Hz µF ü ')
当打印一个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`而不是`char`的API的平行版本来实现。例如,`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);
"W"函数是"真正的"函数。"A"函数存在是为了与基于DOS的Windows向后兼容,并且大部分只是将其字符串参数转换为UTF-16,然后调用相应的"W"函数。
在Unix世界(具体来说是Plan 9),编写一个全新的POSIX API版本被认为是不切实际的,所以对Unicode的支持采用了不同的方法。现有的CJK区域设置中对多字节编码的支持被用来实现一个现在称为UTF-8的新编码。
Unix-like系统对UTF-8的偏好以及Windows对UTF-16的偏好在编写支持Unicode的跨平台代码时是一个巨大的麻烦。Python试图将这一点隐藏在程序员面前,但在控制台打印是Joel的“泄露抽象”的一个例子。
非常有帮助,谢谢。我仍然想知道在Windows的IPython中是否有办法使"print"工作,无论是在内置的Windows控制台中还是在其他第三方控制台中(如果有这样的控制台存在的话)。如果无法显示特殊字符,我希望至少能打印"?"或其他不会崩溃的东西。
是的,Notepad++可以保存为UTF-8,但这似乎不是问题所在。问题是文件的编码与标准输出的编码不匹配。
如果一个模块输出像`u'G\xc3\xb6teborg, Sweden'`这样的字符串,这是不正确的吗?它应该是`u'G\xf6teborg, Sweden'`,或者在编码为UTF-8之后是`'G\xc3\xb6teborg, Sweden'`而不带有`u`。
我相信是的,解决方法是`u'G\xc3\xb6teborg, Sweden'.encode('raw_unicode_escape')` → `'G\xc3\xb6teborg, Sweden'`