chcp 65001 代码页导致程序终止且没有任何错误

2024-05-02

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')

到目前为止,我得出了这样的观察:

  1. 使用命令提示符直接输出是可行的。
  2. 基于 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(使用前将#替换为@)

chcp 65001 代码页导致程序终止且没有任何错误 的相关文章