当我的 Perl 程序在 cmd.exe 中输出 UTF-8 编码字符串时,为啥我会重复最后一个八位字节?
Posted
技术标签:
【中文标题】当我的 Perl 程序在 cmd.exe 中输出 UTF-8 编码字符串时,为啥我会重复最后一个八位字节?【英文标题】:Why am I getting the last octet repeated when my Perl program outputs a UTF-8 encoded string in cmd.exe?当我的 Perl 程序在 cmd.exe 中输出 UTF-8 编码字符串时,为什么我会重复最后一个八位字节? 【发布时间】:2014-06-18 10:13:10 【问题描述】:更新
正如@ikegami 所建议的,我将此报告为一个错误。
Bug #121783 for perl5: Windows: UTF-8 encoded output in cmd.exe with code page 65001 causes unexpected output
考虑以下 C 和 Perl 程序,它们都在标准输出上输出字符串“αβγ”的 UTF-8 编码:
C版:
#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 αβγ
Perl 版本:
C:\...> perl -e "打印 qq\xce\xb1\xce\xb2\xce\xb3\n" αβγ �
据我所知,最后一个八位字节 0xb3
再次在另一行输出,正在转换为 U+FFFD
。
请注意,重定向输出会消除这种影响。
我还可以验证它是重复的最后一个八位字节:
C:\…> perl -e "打印 qq\xce\xb1\xce\xb2\xce\xb3xyz\n" αβγxyz z
另一方面,syswrite 避免了这个问题。
C:\…> perl -e "syswrite STDOUT, qq\xce\xb1\xce\xb2\xce\xb3xyz\n" αβγxyz
我在使用自建 perl 5.18.2 和 ActiveState 5.16.3 的 Windows 8.1 Pro 64 位和 Windows Vista Home 32 位的 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 "打印 qq\xce\xb1\xce\xb2\xce\xb3\n" ╬▒╬▓╬│
当code page is set to 65001code page is set to 65001从perl程序打印时,是什么导致最后一个八位字节输出两次?
PS:我在my blog 上有更多信息和截图。对于这个问题,我尝试将所有内容提炼成最简单的情况。
PPS:省略 \n
会产生更有趣的结果:
C:\…> perl -e "打印 qq\xce\xb1\xce\xb2\xce\xb3xyz" αβγxyzxyz
C:\...> perl -e "打印 qq\xce\xb1\xce\xb2\xce\xb3" αβγ·γ·
【问题讨论】:
我的猜测:Windows shell 中某处的错误。 @FilipeGonçalves 鉴于 C 版本和syswrite
都按预期工作,我怀疑这是 Perl 的 :crlf
IO 层和代码页 65001 之间的交互。但是,我不知道确切的位置看。欢迎指点。
使用perlbug
提交错误报告
如果你忽略了\n
会发生什么?
stdout
,在 Windows 上的文本模式下,是否将 '\n
\ 转换为 "\r\n"
?不知道这将如何解释这一点,但另一个想法。
【参考方案1】:
以下程序产生正确的输出:
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
这似乎表明: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
以进一步诊断此问题。
我examined this a little further。以下是该帖子的一些观察结果:
第一个unix
层的标志是0x01205200 = CANWRITE | TRUNCATE | CRLF | OPEN | NOTREG
。为什么 CRLF
在 Windows 上设置为 unix
层?我对内部结构的了解不足以理解这一点。
但是,第二个unix
层的标志,即由我明确的binmode
推送的标志是 0x01201200 = 0x01205200 & ~CRLF。这对我来说是有意义的。
第一个 crlf 层的标志是0x00c85200 = CANWRITE | TRUNCATE | CRLF | LINEBUF | FASTGETS | TTY
。我在:encoding(utf8)
层之后推送的第二个layer
的标志是0x00c8d200 = 0x00c85200 | UTF8
。
现在,如果我使用 open my $fh, '>:encoding(utf8)', 'ttt'
打开一个文件并转储相同的信息,我会得到:
正如预期的那样,unix
层没有设置CRLF
标志。
【讨论】:
你可能想试试我的 PerlIO::Layers 而不是使用 PerlIO::get_layers,它会提供更友好的输出。:crlf
层中的错误?你肯定在开玩笑。 /s
@Ether 当输出到 cmd.exe 时,它在我看来就像 Windows 上的 unix 层中的一个错误。但是,我不知道为什么PERLIO_F_CRLF is getting set on the bottom-most unix layer。以上是关于当我的 Perl 程序在 cmd.exe 中输出 UTF-8 编码字符串时,为啥我会重复最后一个八位字节?的主要内容,如果未能解决你的问题,请参考以下文章
Windows cmd.exe 命令传递 Perl 系统函数在某些情况下需要双引号吗?
错误MSB6006:“cmd.exe”退出,运行QT应用程序的代码1
Visual Studio C++ 执行 cmd.exe 而不是我的程序 [关闭]