相同的代码,不同机器上有关UTF8字符的不同结果

时间:2014-08-27 12:40:57

标签: perl utf-8

我有这段代码:

use strict;
use warnings;
use utf8;
use HTML::Entities;
use feature 'say';

binmode STDOUT, ':encoding(utf-8)';

my $t1 = "Česká Spořitelna - Q3 2014";
my $t2 =  "Česká Spořitelna - Q3 2014";

say decode_entities($t1);
say decode_entities($t2);

当在我的开发机器上执行时,输出:

Česká Spořitelna - Q3 2014
Česká Spořitelna - Q3 2014

当在UAT机器(Aser Acceptance Test)上执行时,输出:

Äeská SpoÅitelna - Q3 2014
Äeská SpoÅitelna - Q3 2014

现在,在两台机器上,当我运行perl -v时,我们有这是为x86_64-linux-thread-multi-ld构建的perl 5,版本16,subversion 3(v5.16.3)

并且HTML::Entities的版本在两台机器上都相同:

    Installed: 3.69
    CPAN:      3.69  up to date

我的开发机器运行CentOS release 5.8 (Final),UAT机器运行Red Hat Enterprise Linux Server release 5.8 (Tikanga)

编辑(关于locale命令的输出) 它的输出在两台机器上都是相同的:

LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

更新

我在facebook上的perl developers群组中发布了此问题的链接,并从那里得到了一些非常有用的想法:比较两个系统上的输出字节。如果它们相同,那就是显示问题。他们是。现在,有多种方法可以做到这一点

1)

say join ':', map { ord } split //, decode_entities($t1);
say join ':', map { ord } split //, decode_entities($t2);

在两个系统上显示268:101:115:107:225:32:83:112:111:345:105:116:101:108:110:97:32:45:32:81:51:32:50:48:49:52,因此字节相同

2)将$t1$t2输出打印到每个系统上的文件,然后针对这些文件运行hexdump -C并比较输出。此方法还显示文件的内容相同

结论

这是显示问题 - 控制台(putty)无法正确显示字符。  当我们在数据库中添加这些字符时,我们遇到了这个问题,我认为我设法用上面的代码隔离它。你的答案(以及fb中的一些)帮助我发现decode_entities()按预期工作,我们的问题出在其他地方(很可能是在mysql表charset或mysql连接上)。

1 个答案:

答案 0 :(得分:6)

命令终端期望的编码是不同的。如果你想打印UTF-8,你必须设置两个终端以期望UTF-8,例如罗马尼亚语

LANG=ro_RO.UTF-8

以及将STDOUT设置为编码在Perl中的输出方式,例如

binmode STDOUT, ':encoding(utf-8)'

<强>更新

我可以解释发生了什么,虽然为什么这就是我不确定的方式。

取字符串的第一个字符:"\x{010C}"这是一个大写C caron。这是由Perl编码为两个八位字节代码"\x{C4}\x{8C}"并发送到终端,终端在您的开发机器上解码并正确显示它。

然而,在您的测试机器上,终端正在解码编码字符的第一个八位字节 - C4 - 就好像它是ISO-8859-1,一个资本A变音符号。第二个八位字节 - 8C - 被忽略,因为它是该编码中的无效字符。

因此您需要更改终端正在使用的代码页。这样做的方法是按照我的描述设置LANG,但如果 locale 设置正确,我无法解释为什么它不起作用。