问题
当我想在Python解释器中输入 Unicode字符时出现问题(为简单起见,我在示例中使用了a-umlaut,但我首先遇到了这个用于Farsi字符)。每当我将python与chcp 65001
代码页一起使用,然后尝试输入一个Unicode字符时,Python就会退出而没有任何错误。
我花了好几天试图解决这个问题无济于事。但是今天,我在python website上发现了一个帖子,另一个在Lua用户上找到了另一个帖子,虽然没有任何解决方案,但有些人说chcp 65001
是天生就是破碎了。
一劳永逸地知道这个问题是与chcp设计有关还是有可能的解决方法。
重现错误
chcp 65001
Python 3.X:
Python shell
print('ä')
结果:它只是退出shell
然而,这有效python.exe -c "print('ä')"
还有这个:print('\u00e4')
结果:ä
在Luajit2.0.4中
print('ä')
结果:它只是退出shell
然而这有效:print('\xc3\xa4')
到目前为止,我已经提出了这个观察结果:
因此 这不是Python错误和,我们不能在Windows命令提示符中的CLI程序中直接使用Unicode字符,或者像Conemu,Cmder这样的任何Wrapper(我使用Cmder能够看到)并在Windows shell中使用Unicode字符,我没有任何问题)。这是对的吗?
答案 0 :(得分:7)
要在Windows控制台中为Python 2.7和3.x(3.6之前的版本)使用Unicode,请安装并启用win_unicode_console。这使用宽字符函数ReadConsoleW
和WriteConsoleW
,就像其他支持Unicode的控制台程序(如cmd.exe和powershell.exe)一样。对于Python 3.6,添加了一个新的io._WindowsConsoleIO
原始I / O类。它读取和写入UTF-8编码的文本(用于与Unix的跨平台兼容性 - "得到一个字节" - 程序),但在内部它使用宽字符API通过转码到UTF-和从UTF转码16LE。
您在非ASCII输入时遇到的问题可以在控制台中重现所有Windows版本(包括Windows 10)。控制台主机进程,即conhost.exe,不是为UTF设计的-8(代码页65001)并且尚未更新以始终如一地支持它。特别是,非ASCII输入会导致空读。这反过来导致Python的REPL退出并内置input
以引发EOFError
。
问题是conhost在假定单字节代码页的情况下对其UTF-16输入缓冲区进行编码,例如西方语言环境中的OEM和ANSI代码页(例如437,850,1252)。 UTF-8是一种多字节编码,其中非ASCII字符编码为2到4个字节。要处理UTF-8,需要在M / 4
个字符的多次迭代中进行编码,其中M是N字节缓冲区中可用的剩余字节。相反,它假定读取N个字节的请求是读取N个字符的请求。然后,如果输入有一个或多个非ASCII字符,则内部WideCharToMultiByte
调用由于缓冲区过小而失败,并且控制台返回“'成功'读取0个字节。
如果安装了pyreadline模块,您可能无法在Python 3.5中发现这个问题。 Python 3.5会自动尝试导入readline
。在pyreadline的情况下,通过宽字符函数ReadConsoleInputW
读取输入。这是一个读取控制台输入记录的低级函数。原则上它应该有效,但实际上输入print('ä')
会被REPL读作print('')
。对于非ASCII字符,ReadConsoleInputW
返回一系列Alt + Numpad KEY_EVENT
记录。序列是有损的OEM编码,除最后一条记录外,可以忽略该编码,该记录在UnicodeChar
字段中具有输入字符。显然pyreadline忽略了整个序列。
在Windows 8之前,使用代码页65001的输出也被破坏了。它会打印与非ASCII字符数成比例的垃圾文本。在这种情况下,问题是WriteFile
和WriteConsoleA
错误地返回写入屏幕缓冲区的UTF-16代码的数量,而不是UTF-8字节的数量。这会混淆Python的缓冲编写器,导致重复写入它认为剩余的未写入字节。此问题已在Windows 8中修复,作为重写内部控制台API以使用ConDrv设备而不是LPC端口的一部分。较旧版本的Windows可以使用ConEmu或ANSICON来解决此错误。