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

psy*_*hob 4 python windows unicode cmd codepages

问题
当我想在Python解释器中输入 Unicode字符时出现问题(为简单起见,我在示例中使用了a-umlaut,但我首先遇到了这个用于Farsi字符).每当我将python与chcp 65001代码页一起使用,然后尝试输入一个Unicode字符时,Python就会退出而没有任何错误.

我花了好几天试图解决这个问题无济于事.但是今天,我在python网站上发现了一个帖子,另一个在MySQL上发现了一个线程,另一个在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')

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

  1. 使用命令提示符直接输出有效.
  2. 基于Unicode,基于十六进制的角色等效于该角色.

所以, 这不是一个Python的错误我们不能在Windows命令提示符或任何其包装像Conemu,Cmder的CLI程序直接使用Unicode字符(我用Cmder能够看到和在Windows中使用Unicode字符shell和我这样做没有任何问题).它是否正确?

Ery*_*Sun 9

要在Windows控制台中为Python 2.7和3.x(3.6之前的版本)使用Unicode,请安装并启用win_unicode_console.本品采用宽字符函数ReadConsoleWWriteConsoleW,就像其他支持Unicode的控制台程序,例如cmd.exe的和powershell.exe.对于Python 3.6,添加了一个新的io._WindowsConsoleIO原始I/O类.它读取和写入UTF-8编码的文本(用于与Unix的跨平台兼容性 - "获取字节" - 程序),但在内部它通过转码到UTF-16LE和从UTF-16LE转码使用宽字符API.

您在非Windows输入时遇到的问题可以在控制台中重现所有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来解决此错误.