当前位置: 首页 > 知识库问答 >
问题:

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

徐帅
2023-03-14

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

共有3个答案

鲜于允晨
2023-03-14

最近我遇到了UTF-8、CP1252和UTF-8严重混合编码的文件,然后被解释为CP1252,然后又被编码为UTF-8,又被解释为CP1252,等等。

我写了下面的代码,这对我来说很好。它寻找典型的UTF-8字节序列,即使其中一些字节不是UTF-8,而是等效CP1252字节的Unicode表示。

my %cp1252Encoding = (
# replacing the unicode code with the original CP1252 code
# see e.g. http://www.i18nqa.com/debug/table-iso8859-1-vs-windows-1252.html
"\x{20ac}" => "\x80",
"\x{201a}" => "\x82",
"\x{0192}" => "\x83",
"\x{201e}" => "\x84",
"\x{2026}" => "\x85",
"\x{2020}" => "\x86",
"\x{2021}" => "\x87",
"\x{02c6}" => "\x88",
"\x{2030}" => "\x89",
"\x{0160}" => "\x8a",
"\x{2039}" => "\x8b",
"\x{0152}" => "\x8c",
"\x{017d}" => "\x8e",

"\x{2018}" => "\x91",
"\x{2019}" => "\x92",
"\x{201c}" => "\x93",
"\x{201d}" => "\x94",
"\x{2022}" => "\x95",
"\x{2013}" => "\x96",
"\x{2014}" => "\x97",
"\x{02dc}" => "\x98",
"\x{2122}" => "\x99",
"\x{0161}" => "\x9a",
"\x{203a}" => "\x9b",
"\x{0153}" => "\x9c",
"\x{017e}" => "\x9e",
"\x{0178}" => "\x9f",
);
my $re = join "|", keys %cp1252Encoding;
$re = qr/$re/;
my %cp1252Decoding = reverse % cp1252Encoding;
my $cp1252Characters = join "|", keys %cp1252Decoding;

sub decodeUtf8
{
    my ($str) = @_;

    $str =~ s/$re/ $cp1252Encoding{$&} /eg;
    utf8::decode($str);
    return $str;
}

sub fixString
{
    my ($str) = @_;

    my $r = qr/[\x80-\xBF]|$re/;

    my $current;
    do {
        $current = $str;

        # If this matches, the string is likely double-encoded UTF-8. Try to decode
        $str =~ s/[\xF0-\xF7]$r$r$r|[\xE0-\xEF]$r$r|[\xC0-\xDF]$r/ decodeUtf8($&) /eg;

    } while ($str ne $current);

    # decodes any possible left-over cp1252 codes to Unicode
    $str =~ s/$cp1252Characters/ $cp1252Decoding{$&} /eg;
    return $str;
}

除了同样的限制也适用于UTF-8编码字符串之外,这与ikegami的答案具有类似的限制。

白芷阳
2023-03-14

这就是我编写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]) });
}

输出:

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

UTF8是用C/XS编写的,只有在遇到格式错误的UTF-8序列时才会调用回调/回退。

隆礼骞
2023-03-14

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

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

虽然采用了试探法,但它们相当可靠。只有以下情况才会失败:

>

使用iso-8859-1或cp1252编码的
[àáâãäåæçèéêëìíîï]
之一,然后是两个
[€'"...†‡ˆ‰Š‹ŒŽ‘’“”•–—˜ ™ š›œžŸ<代码>

使用iso-8859-1或cp1252编码的一个
[ðñòòõö÷]
之后是两个
[ð()††‡Œ[381;™Š›œſŸ<代码>

使用核心模块Encode可以产生相同的结果,尽管我认为这比安装::Fix拉丁::Fix拉丁::XS编码要慢得多。

$ 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

fix_拉丁语在字符级别工作。如果已知每一行都是使用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

虽然采用了启发式方法,但它们非常可靠。仅当给定行的以下所有条件均为真时,它们才会失败:

>

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

    至少有一个
    [€'"...†‡ˆ‰Š‹ŒŽ‘’“”•–—˜ ™ š›œžŸ<代码>


    [ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß]
    的所有实例总是紧跟在
    [€'"...†‡ˆ‰Š‹ŒŽ‘’“”•–—˜ ™ š›œžŸ<代码>

    所有的
    实例后面总是紧跟着两个
    [(4)††‡710‰èèèèè"]
    ™Š›œſŸ<代码>


    [ðñòóôõö÷]
    的所有实例总是紧跟在
    [€'"...†‡ˆ‰Š‹ŒŽ‘’“”•–—˜ ™ š›œžŸ<代码>

    线路中不存在任何
    [ùùýÿÿ]
    ,并且

    没有任何一个™Š›œſŸ<代码>

    注意事项:

    • Encoding::FixLatin安装命令行工具fix\u-latin来转换文件,使用第二种方法编写一个文件是很简单的
    • 安装Encoding::FixLatin::XS可以加快(函数和文件)的速度
    • 同样的方法也可用于UTF-8与其他单字节编码的混合。可靠性应该是相似的,但可能会有所不同

  •  类似资料:
    • 我正在尝试将我们的数据库从latin1转换为UTF-8。不幸的是,我不能做一个大规模的单一切换,因为应用程序需要保持在线,我们有700GB的数据库要转换。 因此,我试图利用mysql的一些技巧,将表转换为UTF-8,而不是数据。我希望数据能够被实时读取、转换和替换。(如果愿意,可以进行JIT转换) 我们的php应用程序目前使用所有默认值,所以它使用latin1字符集连接到mysql,并丢弃在lat

    • 我试图保存一个字符串在希伯来文文件,同时有文件ANSI编码。恐怕所有的尝试都失败了。 PHP文件本身是UTF-8 这是我正在尝试的代码: 由于某种原因,返回false。 另一次尝试是: 这返回一个空字符串。当这不起作用时,将输出字符集更改为Windows-1255的工作。所以函数本身工作,但由于某种原因,它不转换为1252。 我运行这个函数之前和之后的和打印的结果 在图标之前编码是UTF-8,在图

    • 我有UTF-8源数据,我必须创建在Windows 1252字符编码编码的CSV文件。 我尝试了经典功能,但效果不佳。它必须支持转换后的塞尔维亚字符: "čćžšđ" 。 此外,我还尝试转换为CP1252、ISO-8859-1、ISO-8859-2,功能包括:mb_convert_编码、iconv和iconv_set_编码。 有人知道该试试什么吗?

    • 我正在编写一个PERL脚本,它从一个数据库中读取数据。XLSX Excel文件,并将数据插入Oracle数据库。数据库采用Windows-1252编码,excel文件采用UTF-8编码(据我所知,这是xlsx文件的标准),特殊字符如ö、ü、ű、ő显示为??。正确的转换方法是什么。xlsx文件?在将读取的字符串插入数据库之前,我曾尝试将其转换为windows-1252,并尝试将整个Excel文件转换

    • 我有下面的代码,它会产生令人困惑的输出。。 所以我想看看utf-8编码和windows-1252之间的区别。但当我看输出时,似乎没有区别。只有当我将windows-1252与utf-16进行比较时,才会有区别。 输出: 谁能解释一下为什么utf-8和windows-1252看起来一样? 干杯Alex

    • 我有一个旧数据库,声称已将排序规则设置为windows-1252,并将文本字段的内容存储为 当它显示在遗留的网络应用程序中时,它在浏览器中显示为。浏览器报告一个UTF-8编码的页面。我不知道这种转换是如何完成的(几乎可以肯定的是,它不是通过动态搜索和替换完成的)。这对我来说是个问题,因为我将文本字段(和许多其他类似的字段)从遗留数据库中取出,并放入一个新的UTF-8数据库。新的Web应用程序将新数