修复由UTF-8和Windows-1252组成的文件

ike*_*ami 11 perl encoding character-encoding

我有一个生成UTF-8文件的应用程序,但有些内容编码不正确.一些字符编码为iso-8859-1 aka iso-latin-1或cp1252 aka Windows-1252.有没有办法恢复原始文本?

ike*_*ami 11

是!

显然,最好修复创建文件的程序,但这并不总是可行的.以下是两种解决方案.

一行可以包含多种编码

Encoding :: FixLatin提供了一个名为的函数fix_latin,它解码由UTF-8,iso-8859-1,cp1252和US-ASCII组合而成的文本.

$ perl -e'
   use Encoding::FixLatin qw( fix_latin );
   $bytes = "\xD0 \x92 \xD0\x92\n";
   $text = fix_latin($bytes);
   printf("U+%v04X\n", $text);
'
U+00D0.0020.2019.0020.0412.000A
Run Code Online (Sandbox Code Playgroud)

采用启发式方法,但它们相当可靠.只有以下情况才会失败:

  • 之一的
    [ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞß]
    使用ISO-8859-1或CP1252,随后之一编码
    [€,ƒ"...†‡‰Š<OEZ '''’•--~™S>œžŸ <NBSP>¡¢£¤ 使用iso-8859-1或cp1252编码¥| <SHY>§¨ªª¬¬¯°±³³'μ·¸¹º»¼½¾¿

  • 其中一个
    [àáâåäåæçèéêëìíï]
    使用iso-8859-1或cp1252编码,然后是两个
    [€,ƒ"...†‡‰<ŒŽ''""• - 〜™š>œžŸ¢¢ <NBSP>¥ §©ª«¬ <SHY>®°±²³'μ·¸¹º»¼½¾¿]
    使用ISO-8859-1或CP1252编码.

  • 其中一个
    [ðñòóôõö÷]
    使用iso-8859-1或cp1252编码,然后是两个
    [€,ƒ"...†‡‰<ŒŽ''""• - 〜™š>œžŸ¢¢ <NBSP>¥ 使用iso-8859-1或cp1252编码| <SHY>§ « ¬®¯°±³³'μ·¸¹º»¼½¾¿

使用核心模块Encode可以产生相同的结果,但我想这比安装了Encoding :: FixLatin :: XS的Encoding :: FixLatin慢一点.

$ perl -e'
   use Encode qw( decode_utf8 encode_utf8 decode );
   $bytes = "\xD0 \x92 \xD0\x92\n";
   $text = decode_utf8($bytes, sub { encode_utf8(decode("cp1252", chr($_[0]))) });
   printf("U+%v04X\n", $text);
'
U+00D0.0020.2019.0020.0412.000A
Run Code Online (Sandbox Code Playgroud)

每行只使用一种编码

fix_latin适用于角色等级.如果已知每条线都使用UTF-8,iso-8859-1,cp1252或US-ASCII之一进行完全编码,则可以通过检查线路是否为有效UTF-8来使过程更加可靠.

$ perl -e'
   use Encode qw( decode );
   for $bytes ("\xD0 \x92 \xD0\x92\n", "\xD0\x92\n") {
      if (!eval {
         $text = decode("UTF-8", $bytes, Encode::FB_CROAK|Encode::LEAVE_SRC);
         1  # No exception
      }) {
         $text = decode("cp1252", $bytes);
      }

      printf("U+%v04X\n", $text);
   }
'
U+00D0.0020.2019.0020.00D0.2019.000A
U+0412.000A
Run Code Online (Sandbox Code Playgroud)

采用启发式方法,但它们非常可靠.如果给定行的所有以下内容均为真,则它们将失败:

  • 该行使用iso-8859-1或cp1252进行编码,

  • 至少有一个
    [€,ƒ"...†‡‰<ŒŽ''""• - 〜™š>œžŸ¢¢ <NBSP>¥|§«¬« <SHY>¯®¯°±³³μμ· ¸¹º»¼½¾¿ÀÁÂÃ¼ÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷]
    出现在该行,

  • 所有
    [ÀÁÃ¼ÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞß]的
    实例总是紧跟着
    [€,ƒ"......†‡‰<ŒŽ''""• - 〜™š>œžŸ¢¢ <NBSP>¥|§|ª « <SHY>¬®¯°±³³'μ·¸¹º»¼½¾¿],


  • [àáâããååæçèéêëìíïï]的所有实例
    后面总是紧跟着两个
    [€,ƒ"...†‡‰<ŒŽ''""• - 〜™š>œžŸ¢¢ <NBSP>¥|§«¬«¬ <SHY>®°±²³'μ·¸¹º»¼½¾¿]


  • [ðñòóôõ÷]的所有实例
    后面总是紧跟三个
    [€,ƒ"...†‡‰<ŒŽ''""• - 〜™š>œžŸ¢¢ <NBSP>¥|§«ª« ¬ <SHY>®°±²³'μ·¸¹º»¼½¾¿]

  • 行中没有
    [øùúûüýþÿ]
    ,和

  • 没有
    [€,ƒ"...†‡‰<ŒŽ''""• - 〜™š>œžŸ¢¢ <NBSP>¥|§«¬« <SHY>¬®¯°±³³'μ·¸¹º
    除非前面提到过,否则行中存在»¼½¾¿


笔记:

  • Encoding :: FixLatin安装命令行工具fix_latin来转换文件,使用第二种方法编写一个是很简单的.
  • fix_latin(函数和文件)可以通过安装Encoding :: FixLatin :: XS来加速.
  • 相同的方法可用于UTF-8与其他单字节编码的混合.可靠性应该相似,但可以变化.


cha*_*sen 5

这是我编写Unicode :: UTF8的原因之一.使用Unicode :: UTF8,这在使用Unicode :: UTF8 :: decode_utf8()中的回退选项时是微不足道的.

use Unicode::UTF8 qw[decode_utf8];
use Encode        qw[decode];

print "UTF-8 mixed with Latin-1 (ISO-8859-1):\n";
for my $octets ("\xD0 \x92 \xD0\x92\n", "\xD0\x92\n") {
    no warnings 'utf8';
    printf "U+%v04X\n", decode_utf8($octets, sub { $_[0] });
}

print "\nUTF-8 mixed with CP-1252 (Windows-1252):\n";
for my $octets ("\xD0 \x92 \xD0\x92\n", "\xD0\x92\n") {
    no warnings 'utf8';
    printf "U+%v04X\n", decode_utf8($octets, sub { decode('CP-1252', $_[0]) });
}
Run Code Online (Sandbox Code Playgroud)

输出:

UTF-8 mixed with Latin-1 (ISO-8859-1):
U+00D0.0020.0092.0020.0412.000A
U+0412.000A

UTF-8 mixed with CP-1252 (Windows-1252):
U+00D0.0020.2019.0020.0412.000A
U+0412.000A
Run Code Online (Sandbox Code Playgroud)

Unicode :: UTF8是用C/XS编写的,只在遇到生成错误的UTF-8序列时调用回调/回退.