我有这段代码:
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连接上)。
答案 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 设置正确,我无法解释为什么它不起作用。