perl-修复utf8和拉丁编码的混合问题:使用开放式IO与binmode

时间:2018-11-05 07:25:02

标签: perl utf-8

我有一个perl存储文件,(当带有dumper的dumper时)其中包含以下字符串:

my $str1 = "1 = educa\x{c3}\x{a7}\x{c3}\x{a3}o";
my $str2 =  "2 = educa\x{e7}\x{e3}o";

我一直在尝试制定合理的策略,以输出UTF8(另请参见perl Encode::Guess with and without hints - detecting utf8)。

让我继续上面的perl代码,并获取一些声明:

use 5.18.2;
use Encode qw( encode_utf8 decode_utf8 from_to encode decode);
use Encode::Guess;
use Encoding::FixLatin qw(fix_latin);

sub sayStrings() {
    say fixEnc($_[0]);
    say fixEnc($_[1],'hint');
    say "";
};

sub fixEnc() {
    my $data = $_[0];
    my $enc = "";
    if ($_[1]) {
        $enc = guess_encoding($data, qw/utf8 latin-1/);
    } else {
        $enc = guess_encoding($data);
    };
    if (!ref($enc)) {
        return "ERROR: Can't guess: $enc for $data";
    } else {
        my $flag1a = utf8::is_utf8($data);
        my $flag2a = utf8::valid($data);
        $data .= "; encoding: ".$enc->name.", is_utf8=$flag1a, valid=$flag2a";
        return $data;
    };
};

现在开始提问!我将使用各种摘要来补充该代码。

say "Question 1";
&sayStrings($str1, $str2);

use open IO => ':encoding(UTF-8)';
say "raw";
&sayStrings($str1, $str2);

都给:

Question 1
1 = educação; encoding: utf8, is_utf8=, valid=1
2 = educa??o; encoding: iso-8859-1, is_utf8=, valid=1

问题1A:use open IO => ':encoding(UTF-8)';什么都不做?我猜我的系统已经设置为UTF8。是吗?

问题1B:为什么2中的字符不能正确显示?可以正确检测到编码,但是当字符串以UTF输出时,'çã'成为系统不知道(或不存在)的UTF字符了吗?

现在是问题2:

use open IO => ':encoding(UTF-8)',':std';
say "Question 2";
&sayStrings($str1, $str2);

给予:

Question 2
1 = educação; encoding: utf8, is_utf8=, valid=1
2 = educação; encoding: iso-8859-1, is_utf8=, valid=1

问题2:为什么这样做会使latin-1字符串正确显示,却破坏了UTF8字符串? (即,似乎通过添加:std,将str1中的字符序列解释为latin-1,而不是UFT8,请参见perl Encode::Guess with and without hints - detecting utf8)。为什么会这样?

问题3:

use open IO => ':encoding(UTF-8)',':std';
say "fix_latin";
&sayStrings(&fix_latin($str1), &fix_latin($str2));

给予

fix_latin
1 = educação; encoding: utf8, is_utf8=1, valid=1
2 = educação; encoding: utf8, is_utf8=1, valid=1

问题3:我猜fix_latin指示字符串为utf8,因此字符串可以正确打印。因此,对于将字符串符号发布为utf8和binmode显然有一些我不了解的地方。什么事?

非常感谢!

(P.S。已尝试阅读有关此文档的文档,但是的,请发送链接以解释此问题-理想情况下,以清晰的语言提供大量示例...)

1 个答案:

答案 0 :(得分:2)

首先,您必须认识到$str2可以看作是使用iso-8859-1编码的字符串,而且它也是Unicode Code Points的字符串。这是因为使用iso-8859-1编码的字符串与Unicode代码点的字符串没有区别。例如,decode('iso-8859-1', $str)产生$str。这意味着,向期望使用Unicode代码点的字符串的人提供使用iso-8859-1编码的字符串,向期望使用iso-8859-1的字符串的东西提供Unicode代码点的字符串将起作用(如果所有代码点位于iso-8859-1字符集中。)


  

问题1A:use open IO => ':encoding(UTF-8)';什么都不做?

这将设置open的默认图层。例如,它使

open(my $fh, '>', $qfn)

等同于

open(my $fh, '>:encoding(UTF-8)', $qfn)

由于不使用没有默认图层的open,因此根本不使用open,因此没有效果。


  

问题1B:为什么2中的字符不能正确显示?

您的终端需要UTF-8。

使用UTF-8($str1)编码的字符串包含终端所期望的内容,因此可以正确显示。

使用iso-8859-1($str2)编码的字符串不符合终端的预期,因此显示不正确。


  

问题2:为什么这会使latin-1字符串正确显示,但破坏了UTF8字符串?

您在STDOUT中添加了:encoding(UTF-8)层,因此现在期望打印到STDOUT的字符串包含Unicode代码点,并且它们将使用UTF-8进行编码。

使用UTF-8($str1)编码的字符串不包含print所期望的字符串,因此已被整顿。 (具体来说,它以“双重编码”结尾。)

Unicode代码点($str2)的字符串由print所期望的组成,因此它被正确编码。


  

问题3:我猜fix_latin指示该字符串是utf8,因此该字符串可以正确打印。

内部表示形式(如is_utf8所示)与此处无关(应该如此)。

fix_latin("1 = educa\x{c3}\x{a7}\x{c3}\x{a3}o")产生了"1 = educa\x{e7}\x{e3}o"

fix_latin("2 = educa\x{e7}\x{e3}o")产生了"2 = educa\x{e7}\x{e3}o"

相关问题