问题
当我想在Python解释器中input Unicode字符时出现问题(为了简单起见,我在示例中使用了一个变音符号,但是我已经第一次遇到了这个波斯语字符)。 每当我用chcp 65001
代码页使用python,然后尝试input一个Unicode字符,Python就不会出现任何错误。
我花了好几天的时间试图解决这个问题无济于事。 但是今天,我在Python网站上发现了一个线程,另外一个在MySQL上 ,另一个在Lua用户上,这个突然退出的问题引发了一些问题,尽pipe没有任何解决办法,有人说chcp 65001
本质上是坏的。
一劳永逸地知道这个问题是否与chcp-design-related有关或者有可能的解决方法是很好的。
重现错误
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命令提示符或其任何包装如Conemu,Cmder(我使用Cmder能够看到和使用Windows中的Unicode字符壳和我没有任何问题,所以这样做)。 它是否正确?
要在Python 2.7和3.x的Windows控制台(3.6之前)中使用Unicode,请安装并启用win_unicode_console 。 与其他支持Unicode的控制台程序(如cmd.exe和powershell.exe)一样,它使用宽字符函数ReadConsoleW
和WriteConsoleW
。 对于Python 3.6,添加了一个新的io._WindowsConsoleIO
原始I / O类。 它读取和写入UTF-8编码的文本(与Unix的跨平台兼容性 – “获取一个字节” – 程序),但在内部它使用宽字符的API转码到UTF-16LE和从UTF-16LE。
您在使用非ASCII输入时遇到的问题可以在所有Windows版本(包括Windows 10)中的控制台中重现。控制台主机进程(即conhost.exe)不是针对UTF-8(代码页65001)设计的尚未更新以支持它一贯。 特别是,非ASCII输入会导致读取空。 这反过来导致Python的REPL退出并内置input
来引发EOFError
。
问题在于,假设一个单字节代码页(例如西方语言环境中的OEM和ANSI代码页(例如437,850,1252)),conhost对其UTF-16输入缓冲区进行编码。 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的缓冲作家,导致重复写入它认为是剩余的未写入字节。 作为重写内部控制台API以使用ConDrv设备而不是LPC端口的一部分,在Windows 8中解决了此问题。 较早版本的Windows可以使用ConEmu或ANSICON来解决这个错误。