Windows控制台已经识别了至少十年的Unicode,可能早在Windows NT上。但是由于某些原因,主要的跨平台脚本语言(包括Perl和Python)只输出各种8位编码,因此需要很多麻烦才能解决。 Perl给出了“打印中的宽字符”警告,Python给出了一个charmap错误并退出。为什么在这么多年之后,他们不仅仅是简单地调用输出UTF-16 Unicode的Win32 -W API而不是通过ANSI /代码页瓶颈强制一切?
跨平台性能是否只是低优先级?这些语言是否在内部使用UTF-8并且发现输出UTF-16太麻烦了?或者-WAP固有地破坏到不能按原样使用的程度?
更新
似乎责任可能需要各方共同承担责任。我想象脚本语言只能在Windows上调用wprintf
并让操作系统/运行时担心重定向等问题。但事实证明even wprintf on Windows converts wide characters to ANSI and back before printing to the console!
请告诉我这是否已修复,因为错误报告链接似乎已损坏,但我的Visual C测试代码仍然无法用于wprintf并成功用于WriteConsoleW。
更新2
实际上,您可以使用wprintf
从C打印UTF-16到控制台,但前提是_setmode(_fileno(stdout), _O_U16TEXT)
。
从C你可以将UTF-8打印到一个控制台,其代码页设置为代码页65001,但是Perl,Python,PHP和Ruby都有防止这种情况的错误。 Perl和PHP通过在包含至少一个宽字符的行之后添加额外的空行来破坏输出。 Ruby的损坏输出略有不同。 Python崩溃。
更新3
Node.js是第一个没有出现此问题的脚本语言。
Python开发团队慢慢意识到自it was first reported back at the end of 2007以来这是一个真正的问题,并且已经看到了大量的活动,以便在2016年完全理解并完全修复该错误。
答案 0 :(得分:20)
主要问题似乎是在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接口,并邀请程序员使用它(查看Stack Overflow上最近的一些Windows API问题,大多数新手< em>仍然使用可怕的遗留API!)。
当Unicode出现时,许多人意识到它很有用。 Unicode最初只是一个纯16位编码,所以很自然地使用16位代码单元。微软然后显然说“好吧,我们有这个16位编码,所以我们必须创建一个16位的API”,没有意识到没有人会使用它。然而,Unix名人认为“我们如何以高效和向后兼容的方式将其集成到当前系统中,以便人们真正使用它?”随后发明了UTF-8,这是一个杰出的工程。就像Unix创建时一样,Unix人们想的更多,需要更长时间,在经济上取得更少成功,但最终做得不错。
我无法对Perl发表评论(但我认为Perl社区中的Windows仇恨比Python社区更多),但对于Python,我知道BDFL(不喜欢Windows的人)已经声明在所有平台上提供足够的Unicode支持是一个主要目标。
答案 1 :(得分:9)
对讨论的贡献很小 - 我正在运行捷克本地化的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";
答案 2 :(得分:7)
我必须解决你的许多问题。
你知道吗
chcp 65001
命令来切换终端吗?答案 3 :(得分:5)
迈克尔·卡普兰(Michael Kaplan)有一系列关于cmd
控制台和Unicode的博客文章可能提供信息(虽然没有真正回答你的问题):
Conventional wisdom is retarded, aka What the @#%&* is _O_U16TEXT?
Anyone who says the console can't do Unicode isn't as smart as they think they are
PS:感谢@Jeff找到archive.org链接。
答案 4 :(得分:4)
您确定您的脚本会在其他平台上正确输出Unicode吗? “广泛的印刷品”警告让我非常怀疑。
我建议您查看此overview
答案 5 :(得分:3)
这么多年后为什么要这样做呢 他们不只是简单地调用Win32 -W 输出UTF-16 Unicode的API 而不是强迫一切通过 ANSI /代码页瓶颈?
因为Perl和Python不是Windows程序。他们的Unix程序碰巧主要移植到Windows。因此,除非必要,否则他们不喜欢调用Win32函数。对于基于字节的I / O,没有必要;这可以通过Standard C Libary来完成。基于UTF-16的I / O是一种特殊情况。
或者-WAP本质上是坏的 达到他们无法做到的程度 按原样使用?
我不会说-WAP本质上已经破坏了,就像我说微软在C(++)中使用Unicode的方法本身已经被打破。
无论有多少Windows开发人员坚持认为程序应该使用wchar_t
而不是char
,切换的障碍太多了:
wchar_t
,在其他地方使用UTF-32 wchar_t
。 (新的char16_t
和char32_t
类型可能会有所帮助。)_wfopen
,_wstat
等,限制了在跨平台代码中使用wchar_t
的能力。printf("Hello, world!\n");
学习C,而不是wprintf(L"Hello, world!\n");
。我在大学里使用过的C教科书在附录A.13中都没有提到过广泛的字符。char*
字符串的现有数量的代码行。答案 6 :(得分:2)
要让Perl以这种方式完全支持Windows,必须修改对print
printf
say
warn
和die
的每次调用。
确定后,您必须使用完全不同的API函数集。
如果您真的希望看到正确执行此操作所涉及的所有内容,请查看source的Win32::Unicode::Console。
在Linux,OpenBSD,FreeBSD和类似操作系统上,您通常只需在STDOUT
和STDERR
文件句柄上调用binmode
。
binmode STDOUT, ':utf8';
binmode STDERR, ':utf8';
这假设终端正在使用UTF-8编码。
答案 7 :(得分:2)
对于Python,跟踪器中的相关问题是http://bugs.python.org/issue1602(如评论中所述)。请注意,它开放7年。我尝试将一个有效的解决方案(根据问题中的信息)发布为Python包:https://github.com/Drekin/win-unicode-console,https://pypi.python.org/pypi/win_unicode_console。
答案 8 :(得分:0)
介绍了Win32控制台如何使用Perl以及从ANSI到Unicode的场景后面发生的转码;虽然不仅仅是Perl问题,还会影响其他语言