如何检测Latin1和UTF-8?

4
我正在从XML文件中提取字符串,虽然它应该是纯UTF-8格式的,但实际上不是。我的想法是:
#!/usr/bin/perl
use warnings;
use strict;
use Encode qw(decode encode);
use Data::Dumper;

my $x = "m\x{e6}gtig";
my $y = "m\x{c3}\x{a6}gtig";

my $a = encode('UTF-8', $x);
my $b = encode('UTF-8', $y);

print Dumper $x;
print Dumper $y;
print Dumper $a;
print Dumper $b;

if ($x eq $y) { print "1\n"; }
if ($x eq $a) { print "2\n"; }
if ($a eq $y) { print "3\n"; }
if ($a eq $b) { print "4\n"; }
if ($x eq $b) { print "5\n"; }
if ($y eq $b) { print "6\n"; }

输出

$VAR1 = 'm�gtig';
$VAR1 = 'mægtig';
$VAR1 = 'mægtig';
$VAR1 = 'mægtig';
3

在仅考虑拉丁字符集(latin1)字符串会增加其长度的情况下,对已经使用UTF-8编码的字符串进行编码也会使它更长。因此,我不能通过这种方式来检测拉丁字符集和UTF-8之间的区别。

问题

我希望最终得到的是一个始终为UTF-8的字符串,但如何检测它是拉丁字符集还是UTF-8,以便只转换拉丁字符集字符串?

能够获得字符串是否为UTF-8的肯定或否定答案同样有用。


2
你想要一个猜测正确字符集的解决方案,还是想要准确的结果?因为后者是不可能的。 - deviantfan
如果无法准确地完成,那么猜测总比什么都不做好 =) - Jasmine Lognnes
@ikegami:这仍然是猜测。我并不是说这是不好的,但这并不会改变事实。 - deviantfan
@deviantfan,你似乎误读了一些内容。我从来没有说过这不是猜测。 - ikegami
2
你不能回到提供数据的人那里,要求他们提供有效的UTF8,从而避免所有这些问题吗? - Dave Cross
显示剩余2条评论
1个回答

10

由于UTF-8的一些特性,使用iso-8859-1编码的文本很可能不是有效的UTF-8,除非在两种编码下解码完全相同[1]

因此,解决方法是尝试使用UTF-8进行解码。如果失败了,则改用iso-8859-1进行解码。由于使用iso-8859-1进行解码不会产生任何影响,所以我将跳过这个步骤。

  • utf8::实现:

my $decoded_text = $utf8_or_latin1;
utf8::decode($decoded_text);
  • 编码::实现:

    use Encode qw( decode_utf8 );
    
    my $decoded_text =
       eval { decode_utf8($utf8_or_latin1, Encode::FB_CROAK|Encode::LEAVE_SRC) }
          // $utf8_or_latin1;
    

  • 现在,您说您想要UTF-8。 UTF-8是从编码解码文本获取的。

    • utf8:: 实现:



    • utf8:: 实现:


    my $utf8 = $decoded_text;
    utf8::encode($utf8);
    
  • Encode:: implementation:

  • use Encode qw( encode_utf8 );
    
    my $utf8 = encode_utf8($decoded_text);
    

    注意事项

    1. 假设文本是有效的UTF-8或有效的iso-8859-1,我的解决方案只有在满足以下所有条件时才会猜错:

      • 文本使用iso-8859-1编码(而不是UTF-8),
      • 至少有一个[
        <80><81><82><83><84><85><86><87><88><89><8A><8B><8C><8D><8E><8F>
        <90><91><92><93><94><95><96><97><98><99><9A><9B><9C><9D><9E><9F>
        <NBSP>¡¢£¤¥¦§¨©ª«¬<SHY>®¯°±²³´µ¶·¸¹º»¼½¾¿
        ] 存在,
      • 所有出现[ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß]的地方后面跟着其中之一[
        <80><81><82><83><84><85><86><87><88><89><8A><8B><8C><8D><8E><8F>
        <90><91><92><93><94><95><96><97><98><99><9A><9B><9C><9D><9E><9F>
        <NBSP>¡¢£¤¥¦§¨©ª«¬<SHY>®¯°±²³´µ¶·¸¹º»¼½¾¿],
      • 所有出现[àáâãäåæçèéêëìíîï]的地方后面跟着两个[
        <80><81><82><83><84><85><86><87><88><89><8A><8B><8C><8D><8E><8F>
        <90><91><92><93><94><95><96><97><98><99><9A><9B><9C><9D><9E><9F>
        <NBSP>¡¢£¤¥¦§¨©ª«¬<SHY>®¯°±²³´µ¶·¸¹º»¼½¾¿]

    为什么将UTF8编码转换为UTF8不会破坏它?但在我的操作中确实会。 - Jasmine Lognnes
    在你的例子中它可以工作,但我不明白为什么它可以,在我的代码中却失败了。 - Jasmine Lognnes
    2
    我不使用UTF-8编码字节来编码UTF-8。我根本不编码UTF-8字节。我使用UTF-8对解码文本(Unicode代码点)进行编码。 - ikegami
    @ikegami,您好,能否详细说明一下为什么使用 iso-8859-1 解码是无操作的?为什么不直接在您的 Encode:: 实现中添加以下内容:my $decoded_text = eval { ... } // decode ("iso-8859-1", $utf8_or_latin1);?谢谢。 - n.r.
    1
    @n.r. 关于“为什么使用iso-8859-1解码是无操作?”的问题,这是因为Unicode是iso-8851-1的扩展。具体来说,iso-8859-1 0是代码点0,1是1,2是2,...,FF是FF。 - ikegami

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