【问题标题】:Same code, different results on different machines regarding UTF8 characters相同的代码,不同机器上关于 UTF8 字符的不同结果
【发布时间】:2014-08-27 12:40:57
【问题描述】:

我有这个代码:

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 验收测试),输出:

Ä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)

EDIT(关于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 表字符集或 mysql 连接处)。

【问题讨论】:

  • 检查两台机器上locale命令的输出。
  • 终端需要 UTF-8 以外的内容,但您输出的是 UTF-8。调整终端期望的编码或调整使用的编码使其匹配。
  • 你是如何连接到这些机器的?您是否为两者使用相同的终端程序?它是否具有特定于每个主机的设置?
  • 检查 shell 如何接受 utf8 i/o。例如试试touch ČeskáKočkals。使用 bash?尝试设置set convert-meta off;set input-meta on;set output-meta on

标签: perl utf-8


【解决方案1】:

命令终端期望的编码不同。如果要打印 UTF-8,则必须将两个终端都设置为期望 UTF-8,例如罗马尼亚语

LANG=ro_RO.UTF-8

以及将 STDOUT 设置为 encode 在 Perl 中以这种方式输出,例如

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

更新

我可以解释发生了什么,虽然我不确定为什么会这样。

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

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

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

【讨论】:

  • 我已根据您的建议更新了问题 - 您还有其他想法我应该尝试一下吗?
  • 您的终端几乎可以肯定设置为 ISO-8859-1。看我的更新。查看 LANG 使用 echo $LANG 设置为什么
  • 当我们在数据库中添加这些字符时出现问题。我以为我设法用上面的代码隔离了它。您的回答(以及一些来自 fb)帮助我发现 decode_entities() 按预期工作,问题(可能)与控制台(腻子)有关
  • @TudorConstantin:啊,你没有告诉我们关于 PuTTY 的事!它忽略了LANG 的设置(毕竟,您没有在远程主机上运行 PuTTY)。但是一旦启动你就会看到PuTTY Configuration,如果你选择Window`/Translation,你可以选择UTF-8,问题就解决了。我希望你学会了更好地解释你的问题?没有人会猜到您不是坐在与您描述的两个系统直接连接 的终端前。请编写您自己的解决方案并接受它以将您的问题标记为已解决。
  • 好吧,正如我所说,我对正确显示字符不感兴趣。您的回答帮助我找出了问题,所以我接受您的回答。我用详细信息更新了问题以供进一步参考:)
猜你喜欢
  • 2016-11-08
  • 1970-01-01
  • 2021-08-21
  • 1970-01-01
  • 2017-06-24
  • 2018-02-13
  • 1970-01-01
  • 2019-07-08
  • 1970-01-01
相关资源
最近更新 更多