Problem
当我想要的时候问题就出现了inputPython 解释器中的 Unicode 字符(为简单起见,我在示例中使用了变音符号,但我第一次遇到波斯语字符)。每当我使用 Python 时CHCP 65001 https://ss64.com/nt/chcp.html代码页,然后尝试输入哪怕一个 Unicode 字符,Python 都会退出且不会出现任何错误。
我花了几天时间试图解决这个问题但没有成功。但今天我在网上发现了一个帖子Python网站 https://bugs.python.org/issue1602,另一个在MySQL https://bugs.mysql.com/bug.php?id=66682另一个关于 Lua 用户的问题是关于这种突然退出提出的,尽管没有任何解决方案,有些人说chcp 65001
本质上是破碎的。
如果能一劳永逸地知道这个问题是否与 chcp 设计相关或者是否有可能的解决方法,那就太好了。
重现错误
chcp 65001
Python 3.X:
Python外壳
print('ä')
结果:它刚刚退出shell
However,这有效python.exe -c "print('ä')"
还有这个:print('\u00e4')
结果:ä
在Luajit2.0.4中
print('ä')
结果:刚刚退出shell
然而这有效:print('\xc3\xa4')
到目前为止,我得出了这样的观察:
- 使用命令提示符直接输出是可行的。
- 基于 Unicode、基于十六进制的字符等效项。
So
这不是 Python 错误and我们不能在 Windows 命令提示符或其任何命令提示符的 CLI 程序中直接使用 Unicode 字符wrappers like ConEmu https://en.wikipedia.org/wiki/ConEmu, Cmder https://cmder.net/(我使用 Cmder 是为了能够在 Windows shell 中查看和使用 Unicode 字符,并且我这样做没有任何问题)。它是否正确?
要在 Python 2.7 和 3.x(3.6 之前)的 Windows 控制台中使用 Unicode,请安装并启用win_unicode_console https://pypi.python.org/pypi/win_unicode_console。这使用了宽字符函数ReadConsoleW https://msdn.microsoft.com/en-us/library/ms684958 and WriteConsoleW https://msdn.microsoft.com/en-us/library/ms687401,就像其他支持 Unicode 的控制台程序(例如 cmd.exe 和 powershell.exe)一样。对于 Python 3.6,一个新的io._WindowsConsoleIO
添加了原始 I/O 类。它读取和写入 UTF-8 编码文本(为了与 Unix 跨平台兼容——“获取字节”——程序),但在内部它通过与 UTF-16LE 进行转码来使用宽字符 API。
您在使用非 ASCII 输入时遇到的问题在所有 Windows 版本(包括 Windows 10)的控制台中都可以重现。控制台主机进程(即 conhost.exe)不是为 UTF-8(代码页 65001)设计的,并且尚未更新以持续支持它。特别是,非 ASCII 输入会导致空读取。这反过来会导致Python的REPL退出并内置input
募集EOFError
.
问题是 conhost 假设单字节代码页(例如西方语言环境中的 OEM 和 ANSI 代码页(例如 437、850、1252))对其 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 https://msdn.microsoft.com/en-us/library/ms684961。这是读取控制台输入记录的低级函数。原则上它应该有效,但实际上输入print('ä')
被 REPL 读取为print('')
。对于非 ASCII 字符,ReadConsoleInputW
返回 Alt+Numpad 的序列KEY_EVENT
记录。该序列是有损 OEM 编码,除了最后一条记录(其中包含输入字符)之外,可以忽略该序列。UnicodeChar
场地。显然 pyreadline 忽略了整个序列。
在 Windows 8 之前,使用代码页 65001 的输出也被破坏。它按照非 ASCII 字符的数量按比例打印一串垃圾文本。在这种情况下,问题是WriteFile
and WriteConsoleA
错误地返回写入屏幕缓冲区的 UTF-16 代码数,而不是 UTF-8 字节数。这会让 Python 的缓冲写入器感到困惑,导致重复写入它认为剩余的未写入字节。此问题在 Windows 8 中已修复,作为重写内部控制台 API 以使用 ConDrv 设备而不是 LPC 端口的一部分。旧版本的 Windows 可以使用 ConEmu 或 ANSICON 来解决此错误。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)