返回首页

Ude 西里尔字母错误 C# 编码检测

本文分析了 Ude 库在 C# 中错误检测西里尔编码的问题。提出了一种基于“奇怪”字节的启发式方法来校正 Macintosh/Windows-1251。描述了代码、限制和手动验证推荐。

Ude 错误修复:C# 中无 mojibake 的西里尔文
Advertisement 728x90

解决C#中西里尔文编码检测错误的实用方案

C#中的Ude库在处理包含西里尔字母的文本文件时,偶尔会错误地将编码识别为'Macintosh',而非正确的Windows-1251。这导致读取时出现乱码——本应清晰可读的俄文变成无法辨认的‘摩吉巴克’(mojibake)。此问题不仅出现在Notepad++中,也出现在使用CharsetDetector的代码里。

GetFileEncoding方法中,Ude返回的cdet.Charset为'Macintosh',这会在后续转换时引发错误:

public static Encoding GetFileEncoding(string filename)
{
    Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

    using (FileStream fs = File.OpenRead(filename))
    {
        Ude.CharsetDetector cdet = new Ude.CharsetDetector();
        cdet.Feed(fs);
        cdet.DataEnd();

        if (cdet.Charset != null)
        {
            return Encoding.GetEncoding(cdet.Charset);
        }
    }

    return Encoding.GetEncoding(CodePages.Windows1251);
}

若在ChangeEncodingForMidFile中应用此错误编码,问题将进一步恶化:文本被错误读取并写入新文件,造成数据损坏。

Google AdInline article slot
private static void ChangeEncodingForMidFile(string midFile, 
                                             Encoding midFileEncoding, 
                                             string newFullMidFileName)
{
    using StreamReader reader = new StreamReader(midFile, midFileEncoding);

    using StreamWriter writer = new StreamWriter(
        newFullMidFileName,
        false,
        Encoding.GetEncoding(CodePages.Windows1251)
    );

    writer.Write(reader.ReadToEnd());

    File.Delete(midFile);
    File.Move(newFullMidFileName, midFile);
}

MacCyrillic与Windows-1251编码差异分析

MacCyrillic(CodePages.Macintosh)和Windows-1251编码对西里尔字符使用的字节范围不同。对于不含生僻符号的典型俄文文本,可通过识别‘可疑字节’来判断:这些字节在一种编码中不常见,但在另一种中却很普遍。

  • 对Macintosh而言可疑的字节:Windows-1251中常见的字节(0xAB, 0xB8–0xB9, 0xBB, 0xC0–0xC7, 0xC9, 0xCB–0xCF, 0xD2–0xDB, 0xFF)。
  • 对Windows-1251而言可疑的字节:Macintosh中常见的字节(0x80–0x8F, 0x90–0x97, 0x99–0x9F)。

这些字节集合用于启发式重新验证。

private static readonly ArrayList<byte> StrangeForMacintochBytes = new ArrayList<byte>
{
    0xAB, 
    0xB8, 0xB9, 0xBB,
    0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC9, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
    0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB,
    0xFF
};

private static readonly ArrayList<byte> StrangeForWindows1251Bytes = new ArrayList<byte>
{
    0x80, 0x81, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F
};

实现CustomGetEncoding自定义编码检测

当检测到Macintosh或MacCyrillic时,触发CustomGetEncoding方法:

Google AdInline article slot
if (cdet.Charset != null)
{
    var encoding = Encoding.GetEncoding(cdet.Charset);
    if (encoding.CodePage == CodePages.Macintoch || encoding.CodePage == CodePages.MacCyrillic)
        return CustomGetEncoding(encoding, filename);
}

CustomGetEncoding逻辑如下:

  • 扫描文件字节。
  • 若发现StrangeForMacintochBytes中的任意字节,则返回Windows-1251。
  • 否则,若发现StrangeForWindows1251Bytes中的字节,则返回Macintosh。
  • 若未发现可疑字节,则默认返回Windows-1251。

该方法能有效解决纯俄文文本的编码识别问题,但不具备普适性。

局限性与建议

该方法的局限性:

Google AdInline article slot
  • 仅适用于不含稀有字符的西里尔文文本。
  • 不支持UTF-8或其他编码格式。
  • 依赖启发式规则,非可靠检测机制。

用户应对建议:

  • 在Notepad++中打开文件。
  • 尝试不同编码(如Windows-1251、UTF-8),直到不再出现乱码。
  • 复制文本内容。
  • 创建新文件,使用目标编码粘贴内容并保存。

即使采用此手动方法,若Ude仍误判编码,也无法保证100%成功。

其他字符集检测库同样存在类似问题。自动编码检测本质上是概率性的,依赖于文本中字节频率分布特征。

核心要点

  • Ude在西里尔文文件上常混淆MacCyrillic与Windows-1251。
  • 使用‘可疑字节’启发式规则可在不修改库的前提下修复识别问题。
  • 此方案仅适用于基础俄文文本。
  • 处理前务必在Notepad++中手动确认编码。
  • 字符集检测具有概率性,错误不可避免。

— Editorial Team

Advertisement 728x90

继续阅读