为什么脚本语言不能将Unicode输出到Windows控制台?

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"; 

我不得不回答你的许多问题。

你知道吗

  • Windows为其API使用UTF-16,但仍默认使用用户空间中的各种“有趣的”遗留编码(例如Windows-1252,Windows-1251),包括文件名,对于Windows的许多本地化来说都是不同的?
  • 你需要对输出进行编码,并且为系统选择合适的编码是通过语言环境编译指示来实现的,并且有一个叫做locale的POSIX标准,而且它是在这个标准上建立起来的,Windows与它是不兼容的?
  • Perl已经支持所谓的“宽”API了吗?
  • 微软设法将UTF-8转换成字符编码的代码页系统,并且可以通过发出适当的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 ,切换的障碍就太多了:

  • 平台依赖:
    • 在Windows上使用UTF-16 wchar_t在其他地方使用UTF-32 wchar_t (新的char16_tchar32_t类型可能会有所帮助。)
    • UTF-16文件名函数(如_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 ,并且不得不被修改。

  • 这是Windows吗?
  • 哪个版本的Windows? Perl仍然主要在Windows 95上工作
  • 这是去控制台,还是其他地方。

一旦你确定了,你就必须使用一组完全不同的API函数。

如果你真的想看到正确做这件事的一切,看看Win32 :: Unicode :: Console的来源 。


在Linux,OpenBSD,FreeBSD和类似的操作系统上,你通常可以在STDOUTSTDERR文件句柄上调用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问题,还会影响其他语言