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

问题描述 投票:0回答:1

问题

当我想在 Python 解释器中input Unicode 字符时出现问题(为简单起见,我在示例中使用了变音符号,但我第一次遇到波斯字符)。每当我将 Python 与

CHCP 65001
代码页一起使用,然后尝试输入一个 Unicode 字符时,Python 都会毫无错误地退出。

我花了几天时间试图解决这个问题无济于事。但是今天,我在 Python 网站 上发现了一个线程,另一个在 MySQL 上,另一个在 Lua 用户上发现了关于这个突然退出的问题,尽管没有任何解决方案,有人说

chcp 65001
本身就坏了。

最好一劳永逸地知道这个问题是与 chcp 设计相关还是有可能的解决方法。

重现错误

chcp 65001

Python 3.X:

Python 外壳

print('ä')

结果:它只是退出 shell

然而,这有效

python.exe -c "print('ä')"
还有这个:
print('\u00e4')

结果:ä

在 Luajit2.0.4

print('ä')

结果:它只是退出了 shell

然而这有效:

print('\xc3\xa4')

到目前为止,我已经提出了这个观察:

  1. 使用命令提示符直接输出有效。
  2. 基于 Unicode,基于十六进制的字符等价物。

所以

这不是 Python 错误我们不能在 Windows 命令提示符或其任何 wrappers 中直接在 CLI 程序中使用 Unicode 字符,如 ConEmuCmder(我使用 Cmder 能够在 Windows shell 中查看和使用 Unicode 字符,我这样做没有任何问题)。这是正确的吗?

python windows unicode cmd codepages
1个回答
13
投票

要在 Python 2.7 和 3.x(3.6 之前)的 Windows 控制台中使用 Unicode,请安装并启用 win_unicode_console。这使用宽字符函数

ReadConsoleW
WriteConsoleW
,就像其他支持 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 假定单字节代码页对其 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 的缓冲写入器,导致重复写入它认为是剩余未写入字节的内容。作为重写内部控制台 API 以使用 ConDrv 设备而不是 LPC 端口的一部分,此问题已在 Windows 8 中得到修复。旧版本的 Windows 可以使用 ConEmu 或 ANSICON 来解决这个错误。

© www.soinside.com 2019 - 2024. All rights reserved.