正如@ikegami所build议的,我把这个报告为一个bug。
用于perl5的Bug#121783:Windows:带有代码页65001的cmd.exe中的UTF-8编码输出会导致意外的输出
考虑以下C和Perl程序,它们在标准输出上输出string“αβγ”的UTF-8编码:
#include <stdio.h> int main(void) { /* UTF-8 encoded alpha, beta, gamma */ char x[] = { 0xce, 0xb1, 0xce, 0xb2, 0xce, 0xb3, 0x00 }; puts(x); return 0; }
输出:
C:\ ...> chcp 65001 有效代码页:65001 C:\ ...> cttt.exe αβγ
C:\ ...> perl -e“print qq {\ xce \ xb1 \ xce \ xb2 \ xce \ xb3 \ n}” αβγ
从我可以告诉,最后一个字节, 0xb3
被输出再次,在另一行,正在被转换为U+FFFD
。
请注意,redirect输出消除了这种影响。
我也可以validation它是重复的最后一个八位字节:
C:\ ...> perl -e“print qq {\ xce \ xb1 \ xce \ xb2 \ xce \ xb3xyz \ n}” αβγxyz ž
另一方面, syswrite避免了这个问题。
C:\ ...> perl -e“syswrite STDOUT,qq {\ xce \ xb1 \ xce \ xb2 \ xce \ xb3xyz \ n}” αβγxyz
我已经在Windows 8.1 Pro 64位和Windows Vista Home 32位上使用自build的perl 5.18.2和ActiveState的5.16.3在cmd.exe窗口中观察到这一点。
我在Cygwin,Linux或Mac OS X环境中看不到问题。 另外,Cygwin的perl 5.14.4在cmd.exe中产生正确的输出。
另外,当代码页被设置为437时,C和Perl版本的输出是相同的:
C:\ ...> chcp 437 有效代码页:437 C:\ ...> cttt.exe ╬▒╬▓╬│ C:\ ...> perl -e“print qq {\ xce \ xb1 \ xce \ xb2 \ xce \ xb3 \ n}” ╬▒╬▓╬│
当代码页被设置为65001时,在从cmd.exe的perl程序打印时导致最后一个字节输出两次的原因是什么?
PS: 我的博客上有更多的信息和屏幕截图。 对于这个问题,我试图提炼出最简单的情况。
PPS:离开\n
导致更有趣的事情:
C:\ ...> perl -e“print qq {\ xce \ xb1 \ xce \ xb2 \ xce \ xb3xyz}” αβγxyzxyz
C:\ ...> perl -e“print qq {\ xce \ xb1 \ xce \ xb2 \ xce \ xb3}” αβγγ
以下程序产生正确的输出:
use utf8; use strict; use warnings; use warnings qw(FATAL utf8); binmode(STDOUT, ":unix:encoding(utf8):crlf"); print 'αβγxyz', "\n";
输出:
C:\ ...> chcp 65001 有效代码页:65001 C:\ ...> perl pttt.pl αβγxyz
这似乎表明我有一些funkiness :crlf
crlf图层。 在这一点上,我不明白内部的足够聪明的评论。
经过多次实验,我得出结论,如果控制台已经设置为65001代码页, binmode(STDOUT, ":unix:encoding(utf8):crlf");
将工作”。 但是请注意以下几点:
binmode(STDOUT, ":unix:encoding(utf8):crlf"); print Dump [ map { my $x = defined($_) ? $_ : ''; $x =~ s/\A([0-9]+)\z/sprintf '0x%08x', $1/eg; $x; } PerlIO::get_layers(STDOUT, details => 1) ]; print "αβγxyz\n";
给我:
--- - unix - '' - 0x01205200 - crlf - '' - 0x00c85200 - unix - '' - 0x01201200 - 编码 - utf8 - 0x00c89200 - crlf - '' - 0x00c8d200 αβγxyz
和以前一样,我不知道这个的全部后果。 我打算在某个时候构建一个调试perl
来进一步诊断。
我进一步审查了这一点 。 以下是这篇文章的一些观察:
第一个unix
层的标志是0x01205200 = CANWRITE | TRUNCATE | CRLF | OPEN | NOTREG
0x01205200 = CANWRITE | TRUNCATE | CRLF | OPEN | NOTREG
0x01205200 = CANWRITE | TRUNCATE | CRLF | OPEN | NOTREG
。 为什么CRLF
在Windows上设置为unix
层? 我不知道内部足够了解这一点。
然而,第二个unix
层的标志,我明确的binmode
推送的binmode
是0x01201200 = 0x01205200&〜CRLF。 这对我来说是有意义的。
第一个0x00c85200 = CANWRITE | TRUNCATE | CRLF | LINEBUF | FASTGETS | TTY
层的标志是0x00c85200 = CANWRITE | TRUNCATE | CRLF | LINEBUF | FASTGETS | TTY
0x00c85200 = CANWRITE | TRUNCATE | CRLF | LINEBUF | FASTGETS | TTY
0x00c85200 = CANWRITE | TRUNCATE | CRLF | LINEBUF | FASTGETS | TTY
。 在:encoding(utf8)
图层之后推送的第二layer
标志是0x00c8d200 = 0x00c85200 | UTF8
0x00c8d200 = 0x00c85200 | UTF8
。
现在,如果我使用open my $fh, '>:encoding(utf8)', 'ttt'
打开一个文件并转储相同的信息,我会得到:
--- - unix - '' - 0x00201200 - crlf - '' - 0x00405200 - 编码 - utf8 - 0x00409200
正如所料, unix
层不会设置CRLF
标志。