Perl: 模块中的字符串字面量为latin1编码 - 我想要utf8编码

4
Date::Holidays::DK模块中,某些丹麦假日的名称是用Latin1编码编写的。例如,一月一号是“Nytårsdag”。为了获得适当的utf8编码字符串,我应该对下面的$x执行什么操作?
use Date::Holidays::DK;
my $x = is_dk_holiday(2011,1,1);

我尝试了在use Date::Holidays::DK之前/之后使用各种组合的use utf8no utf8,但似乎没有任何效果。我还尝试使用Encode的decode,但没有成功。更具体地说,

use Date::Holidays::DK;
use Encode;
use Devel::Peek;
my $x = decode("iso-8859-1", 
           is_dk_holiday(2011,1,1)
          );
Dump($x);
print "January 1st is '$x'\n";

输出结果

SV = PV(0x15eabe8) at 0x1492a10
  REFCNT = 1
  FLAGS = (PADMY,POK,pPOK,UTF8)
  PV = 0x1593710 "Nyt\303\245rsdag"\0 [UTF8 "Nyt\x{e5}rsdag"]
  CUR = 10
  LEN = 16
January 1st is 'Nyt sdag'

(在t和s之间有无效字符。)
2个回答

4
使用Date::Holidays::DK之前/之后不要使用utf8,但似乎并没有影响。正确。utf8 pragma只指示程序的源代码是用UTF-8编写的。
我也尝试过使用Encode的decode,但没有成功。你理解错了,实际上你做对了。现在你有了一个Perl字符串,可以对其进行操作。
其中t和s之间有无效字符,这也被你误解了,它实际上是å字符。
你想输出UTF-8,所以你缺少编码步骤。
my $octets = encode 'UTF-8', $x;
print $octets;

请阅读http://p3rl.org/UNI了解编码主题的介绍。您始终需要显式或隐式地进行解码和编码。

2
use utf8 是给 perl 解释器/编译器的一个提示,说明您的文件是 UTF-8 编码的。如果您有带高位设置的字符串,它会自动将它们编码为 Unicode。
如果您有一个以 iso-8859-1 编码的变量,则必须对其进行解码。然后您的变量就在内部 unicode 格式中了。这是 utf8,但您不需要关心 perl 内部使用的编码格式。
现在,如果您想打印这样的字符串,您需要将 Unicode 字符串转换回字节字符串。您需要对此字符串执行 encode 操作。如果您不手动进行编码,perl 本身将把它重新编码为 iso-8859-1。这是默认编码。
在打印变量 $x 之前,您需要对其执行 $x = encode('UTF-8', $x) 操作。
为了正确处理 UTF-8,您始终需要对每个 I/O 上的外部输入进行解码()。并且您始终需要对离开您的程序的所有内容进行编码()。
要更改默认的输入/输出编码,您可以使用类似以下的代码。
use utf8;
use open ':encoding(UTF-8)';
use open ':std';

第一行表示您的源代码采用utf8编码。第二行表示每个输入/输出都应自动采用utf8编码。需要注意的是,open()也会以utf8模式打开文件。如果您使用二进制文件,则需要在句柄上调用binmode()
但第二行不会改变STDIN、STDOUT或STDERR的处理方式。第三行将更改这一点。
您可以使用模块utf8:all,使此过程更加简便。但了解所有这些背后的工作原理总是很好的。
要更正您的示例,可能有以下一种方法:
#!/usr/bin/env perl
use Date::Holidays::DK;
use Encode;
use Devel::Peek;
my $x = decode("iso-8859-1", 
           is_dk_holiday(2011,1,1)
          );
Dump($x);
print encode("UTF-8", "January 1st is '$x'\n");

1
希望您能删除关于is_utf8的段落。 - daxim
你知道一个更好的方法来检查一个字符串是否内部编码为Unicode吗?如果有,那我会进行替换。 - David Raab
1
你应该说“内部编码为UTF-8 编码”,因为像Unicode这样的字符集编码根本没有意义。至于如何回答:你不需要关心,SvUTF8标志或其缺失不能告诉你(这就是is_utf8实际检查的内容)。程序员只需要跟踪以下几点:我已经解码了传入的八位字节吗?我已经编码了传出的字符数据吗?Perl如何在内部编码字符数据是它自己的事情(比你想象的要复杂),你不应该干扰utf8模块中的函数。它的文档也是这么说的。 - daxim
如果你想编写一个能够正确处理Unicode字符串并与外部世界交互的模块,那么你需要知道一个字符串是否被编码为Unicode(是的,Unicode不是一种编码方式,在内部它是UTF-8,但用户不应该关心内部表示,用户只需要关心它是否是Unicode)。当然,你也可以不关心Unicode,让使用你的模块的用户自己处理,但我不喜欢这样。Perl有Unicode字符串,模块作者应该考虑到它们。我总是乐于接受更好的方法。“不要这样做”不是更好的方法。 - David Raab
3
抱歉,但那完全不正确。is_utf8并不表示某些内容需要被编码。事实上,Perl不能知道字符串是否需要被编码。如果能知道,它就可以自己完成这个任务。(我可以详细地反驳你的说法,但是这个框不适合解释任何东西。)至于替代方案,你应该在输入时对所有内容进行解码,在输出时对所有内容进行编码。如果你想处理已编码和未编码的字符串,你需要手动跟踪哪个是哪个。 - ikegami
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接