Windows控制台至less有十年的时间了,而且可能早于Windows NT。 然而由于某些原因,包括Perl和Python在内的主要跨平台脚本语言只能输出各种8位编码,需要很多麻烦才能解决。 Perl给出了一个“宽字符打印”警告,Python给出一个charmap错误并退出。 为什么这些年来,他们不只是简单地调用输出UTF-16 Unicode的Win32-W API,而不是通过ANSI /代码页瓶颈强制所有的东西?
仅仅是跨平台性能低优先级? 难道这些语言在内部使用UTF-8,并且发现它太费心地输出UTF-16了吗? 或者是-W API固有地破坏到不能被原样使用的程度?
UPDATE
似乎这个责任可能需要各方分担。 我想到脚本语言只能在Windows上调用wprintf
,并让操作系统/运行时间担心redirect等问题。 但事实certificate, 即使Windows上的wprintf转换宽字符为ANSI,然后再打印到控制台 !
请让我知道,如果这已被修复,因为错误报告链接似乎中断,但我的Visual Ctesting代码仍然失败的wprintf和WriteConsoleW成功。
更新2
实际上你可以使用wprintf
从C语言打印UTF-16到控制台,但是只有当你第一次使用_setmode(_fileno(stdout), _O_U16TEXT)
。
从C中可以打印UTF-8到代码页设置为代码页65001的控制台,但是Perl,Python,PHP和Ruby都有这样的错误。 Perl和PHP通过在包含至less一个宽字符的行之后添加额外的空行来破坏输出。 Ruby有不同的错误输出。 Python崩溃。
更新3
Node.js是第一个没有这个问题的脚本语言。
Python开发团队慢慢地意识到这是一个真正的问题,因为它是在2007年底第一次报告的,并且看到了大量的活动来充分理解和完全修复2016年的bug。
主要的问题似乎是,Windows上只能使用标准的C库而不使用平台相关或第三方扩展来使用Unicode。 你提到的语言来源于Unix平台,其实施Unicode的方法与C(它们使用普通的char*
字符串,C语言环境函数和UTF-8)很好地融合在一起。 如果你想在C语言中做Unicode,那么你或多或少不得不写一切东西两次:一次使用非标准的Microsoft扩展,一次对所有其他操作系统使用标准C API函数。 虽然这可以完成,但通常没有高优先级,因为它很麻烦,大多数脚本语言开发人员无论如何都讨厌或忽略Windows。
在更技术层面上,我认为大多数标准库设计人员所做的基本假设是,所有I / O流本质上都是基于字节的,在操作系统级别上,这对所有操作系统上的文件以及Unix上的所有流类似的系统,Windows控制台是唯一的例外。 因此,如果要结合Windows控制台I / O,许多类库和编程语言标准的架构必须在很大程度上进行修改。
另一个更主观的观点是,微软只是不足以推动Unicode的使用。 第一个支持Unicode的Windows操作系统(支持它的时间)是1993年发布的Windows NT 3.1,早在Linux和OS X开始支持Unicode之后。 尽管如此,在这些操作系统中向Unicode的过渡却更加无缝和无争议。 微软再次听取了销售人员而不是工程师的意见,并将技术上过时的Windows 9x保留至2001年; 而不是迫使开发者使用一个干净的Unicode接口,他们仍然提供破碎的,现在不必要的8位API接口,并邀请程序员使用它(看看堆栈溢出最近的一些Windows API问题,大多数新手仍然使用可怕的遗留API!)。
当Unicode出来时,许多人意识到这是有用的。 Unicode开始是一个纯粹的16位编码,所以使用16位代码单元是很自然的。 微软然后显然说:“好吧,我们有这个16位编码,所以我们必须创建一个16位API”,没有意识到没有人会使用它。 然而,Unix界的佼佼者却认为,“我们怎样才能以高效,后向兼容的方式将其融入到当前系统中,以便人们真正使用它? 并随后发明了UTF-8,这是一个辉煌的工程。 就像Unix创建时一样,Unix的人想得更多,需要更长的时间,财务上的成功更少,但是最终做到了这一点。
我不能评论Perl(但是我认为Perl社区比Windows社区有更多的Windows仇恨),但是关于Python,我知道BDFL(谁不喜欢Windows)已经说明了足够的Unicode支持在所有平台上是一个主要目标。
对讨论的小贡献 – 我正在运行捷克本地化的Windows XP,它几乎在任何地方都使用CP1250代码页。 控制台有趣的是,它仍然使用传统的DOS 852代码页。
我能够使用非常简单的perl脚本来打印utf8编码的数据到控制台:
binmode STDOUT, ":utf8:encoding(cp852)";
尝试了各种选项(包括utf16le),但只有以上设置正确打印重音捷克语字符。
编辑:我玩了一些问题,发现了Win32 :: Unicode 。 该模块导出的函数printW
在输出和重定向中都能正常工作:
use utf8; use Win32::Unicode; binmode STDOUT, ":utf8"; printW "Příliš žluťoučký kůň úpěl ďábelské ódy";
我不得不回答你的许多问题。
你知道吗
chcp 65001
命令来切换终端。 Michael Kaplan有一系列有关cmd
控制台和Unicode的博客文章,这些博客文章可能是有用的(虽然没有真正回答你的问题):
传统智慧迟缓,又名@#%&*是_O_U16TEXT?
任何说控制台不能做Unicode的人都不像他们认为的那么聪明
情况的汇合留下了一块石头…
PS:谢谢@Jeff找到archive.org链接。
你确定你的脚本能正确地在其他平台上输出Unicode吗? “印刷中的宽字符”警告让我非常怀疑。
我建议查看这个概述
为什么这些年来,他们不只是简单地调用输出UTF-16 Unicode的Win32-W API,而是通过ANSI / codepage瓶颈来强制所有的东西?
因为Perl和Python不是Windows程序。 他们是Unix程序,几乎已经被移植到Windows。 因此,除非必要,否则他们不喜欢调用Win32函数。 对于基于字节的I / O,这是没有必要的; 这可以用标准C语言库来完成。 基于UTF-16的I / O是一个特例。
或者是-W API固有地破坏到不能被原样使用的程度?
我不会说,-W API本质上已经被打破了,就像我说的那样,微软在C(++)中的Unicode方法本质上已经被破坏了。
无论有多少Windows开发人员坚持认为程序应该使用wchar_t
而不是char
,切换的障碍就太多了:
wchar_t
在其他地方使用UTF-32 wchar_t
。 (新的char16_t
和char32_t
类型可能会有所帮助。) _wfopen
, _wstat
等)的_wstat
限制了在跨平台代码中使用wchar_t
的能力。 printf("Hello, world!\n");
学习C printf("Hello, world!\n");
,而不是wprintf(L"Hello, world!\n");
。 我在大学使用的C教科书从未提到宽字符,直到附录A.13。 char*
字符串的代码行。 为了让Perl完全支持Windows,每一个print
printf
调用都会warn
,并且不得不被修改。
一旦你确定了,你就必须使用一组完全不同的API函数。
如果你真的想看到正确做这件事的一切,看看Win32 :: Unicode :: Console的来源 。
在Linux,OpenBSD,FreeBSD和类似的操作系统上,你通常可以在STDOUT
和STDERR
文件句柄上调用binmode
。
binmode STDOUT, ':utf8'; binmode STDERR, ':utf8';
这假定终端正在使用UTF-8编码。
对于Python,跟踪器中的相关问题是http://bugs.python.org/issue1602 (如评论中所述)。 请注意,这是开放7年。 我试图发布一个工作解决方案(基于问题中的信息)作为Python包: https : //github.com/Drekin/win-unicode-console,https : //pypi.python.org/pypi/win_unicode_console 。
在Perl中的Unicode问题
涵盖了Win32控制台如何与Perl协同工作,以及从ANSI到Unicode的场景后面的代码转换;尽管不仅仅是Perl问题,还会影响其他语言