2011-04-17 82 views
11
作品有时

这里有一个小节目:帮助我理解了为什么Unicode的唯一与Python

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

print('abcd kΩ ☠ °C √Hz µF ü ☃ ♥') 
print(u'abcd kΩ ☠ °C √Hz µF ü ☃ ♥') 

在Ubuntu,GNOME终端,IPython中没有我所期待的:

In [6]: run Unicodetest.py 
abcd kΩ ☠ °C √Hz µF ü ☃ ♥ 
abcd kΩ ☠ °C √Hz µF ü ☃ ♥ 

我得到的结果相同如果我在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) 

反之,在Windows IDLE轧液机的第一命令的输出,但不抱怨第二:

>>> 
abcd kΩ ☠°C √Hz µF ü ☃ ♥ 
abcd kΩ ☠ °C √Hz µF ü ☃ ♥ 

IPython的在Windows命令提示或通过Python的(X,Y)的Console2版本二者裂伤第一输出和抱怨第二:

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> 

IPython的内部的Python(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> 

(在sitecustomize.py,Spyder的设置它基于本地化模块的编码,这是cp1252用于Windows自带SPYDER_ENCODING 7.)

什么给?我的一个命令是错的吗?为什么一个人在某些平台上工作,另一个人在其他平台上工作?如何一致地打印Unicode字符而不会崩溃或搞砸?

是否有一个替代终端的Windows,其行为像Ubuntu中的一个?看来TCC-LE,Console2,Git Bash,PyCmd等都只是cmd.exe的包装而不是替代品。有没有办法在IDLE使用的界面内运行IPython?

+2

在IPython unicode不幸被打破。我们应该在0.11版本的下一个版本中修复它,所以它的行为就像在一个原始的Python解释器中输入一样。 – 2011-04-18 23:00:32

+0

检查[this](http://stackoverflow.com/q/39528462/5284370)。 – Soorena 2016-09-18 13:21:22

回答

10

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

有关此区别的进一步说明,请参见Dive into Python 3 chapter on strings

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编码缺少字符☠☃♥
  • windows-1252编码通过的Spyder使用缺少字符Ω☠√☃♥

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

What gives?

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

最初,它们的工作方式几乎相同:每个语言环境都有自己的语言特定的基于char的编码(Windows中的“ANSI代码页”)。西方语言使用ISO-8859-1或Windows 1252,俄罗斯使用KOI8-R或Windows-1251等

当Windows NT增加了支持对Unicode(INT初期,假设时统一将使用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的新编码。

在编写支持Unicode的跨平台代码时,类Unix系统和Windows上的UTF-16对UTF-8的偏好是一个巨大的痛苦。 Python试图从程序员那里隐瞒这一点,但printing to the console是Joel的“泄漏抽象”之一。

+0

这非常有帮助,谢谢。我仍然想知道是否有办法在Windows中的IPython中进行“打印”,无论是在内置的Windows控制台还是其他第三方控制台(如果存在这种情况)。如果无法显示特殊字符,我至少要打印“?”或者没有崩溃的东西。 – endolith 2011-04-18 21:08:17

+0

@christian:是的,Notepad ++可以用UTF-8保存,但这似乎并不是问题。问题是文件的编码与stdout的编码不匹配。 – dan04 2011-04-18 21:58:11

+0

如果一个模块正在输出像'u'G \ xc3 \ xb6teborg,Sweden''这样的字符串,这不正确吗?它应该是'u'G \ xf6teborg,Sweden'​​',或者在编码为UTF-8之后,没有'u'的''G \ xc3 \ xb6teborg,Sweden'​​'。 – endolith 2011-04-24 00:21:37

0

您的问题在于您的程序需要输出UTF-8字符,但控制台和Web上的各种python转轮使用其他代码页。 无法修改所有编码中的特殊字符。但是,如果你选择使用UTF-8 无处不在,你应该是安全的。

我认为Windows中的任何终端都会这样做 - 所以不要因为这个原因而打开默认的(cmd.exe)。相反,将终端的编码更改为UTF-8,以匹配您的python脚本的编码。

不幸的是,我从来没有找到一种方法来将代码页设置为默认的UTF-8,所以每次打开新的命令提示符时都必须完成此操作。但它通过一个简单的命令来完成,所以这只是一半坏...您可以通过switching codepage更改编码:

>chcp 65001 
Current codepage is now 65001 

请注意,您必须使用标准字体之一这个工作。网络上的大多数资源似乎都暗示了Lucida Console。

+0

现在,我尝试的每个命令都由于'C:\ Python27 \ lib \ site-packages \ IPython \ iplib'中的'line = raw_input_original(prompt).decode(self.stdin_encoding)'而导致'LookupError:unknown encoding:cp65001'失败。 pyc' – endolith 2011-04-17 18:32:41

+3

不幸的是,'chcp 65001'有很多问题。 Microsoft C运行时和默认的Windows控制台设计用于使用特定于语言环境的代码页;当其他人都转向使用UTF-8时,这是一个真正的耻辱。 – bobince 2011-04-18 20:30:24

2

有两个可能的原因:通过print

  • 编码的Unicode的。你不能输出原始的Unicode,所以print需要弄清楚如何将其转换为控制台预期的字节流(它使用sys.stdout.encoding AFAIK),这使我们能够控制台支持
  • 。 Python不控制你的终端,所以如果它在你的终端期望别的东西时吐出UTF-8,你会得到错误的输出。
0

从Python到Windows控制台的Unicode输出不起作用。无法说服Python发出期望宽字符和UCS2的本机Windows编码。

+2

我很高兴被拒绝在这里投票,因为这意味着我错了,最终能够在Windows控制台中获得良好的unicode支持。现在我只是在等待如何做到这一点的细节。 – 2011-04-17 19:03:37

+1

呃...你甚至不能用标准的C运行时输出UCS-2,它总是使用特定于语言环境的ASCII超集代码页(从来没有任何类型的UTF)。有一个独立的Win32专用接口,可用于输出Unicode内容“WriteConsoleW”,但您必须决定输出字节还是字符是您的意思,这可能取决于平台,或者您的IO流正被重定向到文件。这一切都是一团糟。 – bobince 2011-04-18 20:35:00

+0

@bobince事实证明这是一个由Michael Kaplan揭露的神话:http://blogs.msdn.com/b/michkap/archive/2008/03/18/8306597.aspx为'_O_U16TEXT'唱歌! – 2011-04-18 20:38:52

0

@ dan04:你说得对,问题是文件的编码与stdout的编码不匹配。尽管如此,解决该问题的一种方法是更改​​文件的编码。因此,在Windows上,Notepad ++可以用UTF-8字符编码保存代码。

另一种方法是GNU recode。