检测错误的UTF-8编码:要嗅探的坏字符列表?

Mic*_*ael 9 encoding utf-8 iso-8859-1

我在两个应用程序之间共享一个sql-server 2010数据库.我们控制的一个应用程序,另一个应用程序是首先创建数据库的第三方应用程序.我们的应用程序是基于第三方Webmail应用程序构建的CRM.

该数据库包含varchar列,并且是latin-1编码的.第三方应用程序是用PHP编写的,并不关心正确编码数据,所以它将utf-8编码的字节填充到varchar列中,在那里它们被解释为latin-1并且看起来像垃圾.

我们的CRM应用程序是用.Net编写的,它自动检测数据库排序规则与内存中字符串的编码不同,因此当.Ne​​t写入数据库时​​,它会转换字节以匹配数据库编码.

所以...从我们的应用程序写入数据库的数据在数据库中看起来是正确的,但来自第三方应用程序的数据却没有.

当我们的应用程序写入FirstName =Céline时,它将作为Céline存储在数据库中

当webmail应用程序写入FirstName =Céline时,它将作为Céline存储在db中

我们的CRM应用程序需要显示在任一系统中创建的联系人.所以我正在编写一个EncodingSniffer类,它查找标记的字符,指示其编码不良的字符串并转换它们.

目前我有:

private static string[] _flaggedChars = new string[] { 
            "é" 
        };

这对于显示Céline作为Céline很有用,但我需要添加到列表中.

有没有人知道一个资源,以获得utf-8特殊字符可以解释为iso-8859-1的所有可能方式?

谢谢

澄清: 因为我在.Net工作.当从数据库加载到内存中时,该字符串将转换为Unicode UTF-16.因此,无论它是否在数据库中正确编码.它现在表示为UTF16字节.我需要能够分析UTF-16字节,并确定它们是否因为utf-8字节被塞入iso-8859-1数据库而被搞砸了....清楚如泥吧?

这是我到目前为止所拥有的.它清理了大多数错误编码字符的显示,但我仍然遇到É问题:Éric通过webmail存储在数据库中,但是在检测到错误编码并将其更改回来后,它显示为 ? ric看着有2500个联系人的用户,其中有数百个有编码问题,É是唯一没有正确显示的东西......

public static Regex CreateRegex()
    {
        string specials = "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö";

        List<string> flags = new List<string>();
        foreach (char c in specials)
        {
            string interpretedAsLatin1 = Encoding.GetEncoding("iso-8859-1").GetString(Encoding.UTF8.GetBytes(c.ToString())).Trim();//take the specials, treat them as utf-8, interpret them as latin-1
            if (interpretedAsLatin1.Length > 0)//utf-8 chars made up of 2 bytes, interpreted as two single byte latin-1 chars.
                flags.Add(interpretedAsLatin1);
        }

        string regex = string.Empty;
        foreach (string s in flags)
        {
            if (regex.Length > 0)
                regex += '|';
            regex += s;
        }
        return new Regex("(" + regex + ")");
    }

    public static string CheckUTF(string data)
    {
        Match match = CreateRegex().Match(data);
        if (match.Success)
            return Encoding.UTF8.GetString(Encoding.GetEncoding("iso-8859-1").GetBytes(data));//from iso-8859-1 (latin-1) to utf-8
        else
            return data;
    }
Run Code Online (Sandbox Code Playgroud)

所以:É正在转换为195'Ã',8240'‰'

Cel*_*ada 1

您可能应该尝试将字节字符串解码为 UTF-8,如果出现错误,则假设它是 ISO-8859-1。

编码为 ISO-8859-1 的文本很少“碰巧”也是有效的 UTF-8...除非 ISO-8859-1 实际上只包含 ASCII,但在这种情况下,您不会遇到问题当然,全部。所以这个方法相当稳健。

忽略哪些字符在实际语言中比其他字符出现得更频繁,这里是一个简单的分析,假设每个字符以相同的频率出现。让我们尝试找出有效的 ISO-8859-1 被误认为 UTF-8 并导致 mojibake 的频率。我还假设 C1 控制字符(U+0080 到 U+009F)不会出现。

对于字节串中的任何给定字节。如果字节接近字符串末尾,那么您更有可能检测到格式错误的 UTF-8,因为某些字节序列的长度不足以成为有效的 UTF-8。但假设该字节不在字符串末尾附近:

  • p(字节解码为 ASCII)= 0.57。这没有提供有关字符串是 ASCII、ISO-8859-1 还是 UTF-8 的信息。
  • 如果此字节是 0x80 到 0xc1 或 0xf8 到 0xff,则它不可能是 UTF-8,因此您将检测到这一点。p=0.33
  • 如果第一个字节是 0xc2 到 0xdf (p=0.11),那么它可能是有效的 UTF-8,但前提是它后面跟着一个值在 0x80 和 0xbf 之间的字节。下一个字节不在该范围内的概率为 192/224 = 0.86。所以UTF-8在这里失败的概率是0.09
  • 如果第一个字节是 0xe0 到 0xef,那么它可能是有效的 UTF-8,但前提是它后面跟着 2 个连续字节。因此,您检测到错误 UTF-8 的概率为 (16/224)*(1-(0.14*0.14)) = 0.07
  • 与 0xf0 到 0xf7 类似,概率为 (8/224)*(1-(0.14*0.14*0.14)) = 0.04。

对于长字符串中的每个字节,检测到错误 UTF-8 的概率为 0.33+0.09+0.07+0.04 = 0.53。

因此,对于长字符串,ISO-8859-1 静静地通过 UTF-8 解码器的概率非常小:每增加一个字符,它大约减半!

当然,此分析假设随机 ISO-8859-1 字符。在实践中,误检率不会那么好(主要是因为现实世界文本中的大多数字节实际上都是 ASCII),但它仍然非常好。