【问题标题】:How to correctly determine character encoding of text files?如何正确判断文本文件的字符编码?
【发布时间】:2014-01-11 20:42:25
【问题描述】:

这是我的情况:我需要正确确定给定文本文件使用哪种字符编码。希望它可以正确返回以下类型之一:

enum CHARACTER_ENCODING
{
    ANSI,
    Unicode,
    Unicode_big_endian,
    UTF8_with_BOM,
    UTF8_without_BOM
};

到目前为止,我可以通过调用以下函数正确判断文本文件是Unicode、Unicode big endian 或UTF-8 with BOM。如果给定的文本文件最初不是UTF-8 without BOM,它还可以正确确定ANSI。 问题是当文本文件为UTF-8 without BOM时,下面的函数会误认为是ANSI文件。

CHARACTER_ENCODING get_text_file_encoding(const char *filename)
{
    CHARACTER_ENCODING encoding;

    unsigned char uniTxt[] = {0xFF, 0xFE};// Unicode file header
    unsigned char endianTxt[] = {0xFE, 0xFF};// Unicode big endian file header
    unsigned char utf8Txt[] = {0xEF, 0xBB};// UTF_8 file header

    DWORD dwBytesRead = 0;
    HANDLE hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        hFile = NULL;
        CloseHandle(hFile);
        throw runtime_error("cannot open file");
    }
    BYTE *lpHeader = new BYTE[2];
    ReadFile(hFile, lpHeader, 2, &dwBytesRead, NULL);
    CloseHandle(hFile);

    if (lpHeader[0] == uniTxt[0] && lpHeader[1] == uniTxt[1])// Unicode file
        encoding = CHARACTER_ENCODING::Unicode;
    else if (lpHeader[0] == endianTxt[0] && lpHeader[1] == endianTxt[1])//  Unicode big endian file
        encoding = CHARACTER_ENCODING::Unicode_big_endian;
    else if (lpHeader[0] == utf8Txt[0] && lpHeader[1] == utf8Txt[1])// UTF-8 file
        encoding = CHARACTER_ENCODING::UTF8_with_BOM;
    else
        encoding = CHARACTER_ENCODING::ANSI;   //Ascii

    delete []lpHeader;
    return encoding;
}

这个问题困扰了我很长时间,我仍然找不到好的解决方案。任何提示将不胜感激。

【问题讨论】:

  • 术语“ANSI”经常被错误地用来指代 8 位编码,通常是 Windows 特定的编码之一,例如 Windows-1252,它从未成为 ANSI 标准。在微软的世界里,“Unicode”这个词经常被错误地用来指代 UTF-16 编码。 Unicode 不是一种编码,但是有几种编码可以用来表示 Unicode。 ASCII 文件与 UTF-8 文件无法区分,而 UTF-8 文件恰好包含 0..127 范围之外的任何字符。大多数 UTF-8 文件不以 BOM 开头(因为 UTF-8 没有字节顺序)。
  • 不要在注释中枚举编码类型,而是在enum中枚举它们。

标签: c++ text character-encoding


【解决方案1】:

对于初学者来说,没有像“Unicode”这样的物理编码。您的意思可能是 UTF-16。其次,任何文件在“ANSI”或任何单字节编码中都是有效的。您唯一能做的就是猜测以最有可能抛出无效匹配的最佳顺序。

您应该按以下顺序检查:

  • 开头是否有 UTF-16 BOM?那么它可能是UTF-16。使用 BOM 指示它是大端还是小端,然后检查文件的其余部分是否符合。
  • 开头是否有 UTF-8 BOM?那么它可能是UTF-8。检查文件的其余部分。
  • 如果以上没有产生正匹配,请检查整个文件是否是有效的 UTF-8。如果是,则可能是 UTF-8。
  • 如果上述结果没有得到肯定匹配,则可能是 ANSI。

如果您也希望 UTF-16 文件 没有 BOM(例如,在 XML 声明中指定编码的 XML 文件是可能的),那么您必须在其中插入该规则也是。尽管上述任何一项都可能产生误报,但会将 ANSI 文件错误地识别为 UTF-*(尽管它不太可能)。您应该始终拥有 元数据 来告诉您文件的编码方式,事后检测到它是不可能的 100% 准确。

【讨论】:

  • 我刚刚注意到,在 Notepad++ 中,没有UTF-16。相反,它还有两种类型:UCS-2 Big Endian 和 UCS-2 Little Endian。那么UTF-16 是否等同于UCS-2?
  • 不,UCS-2 是一种较旧的 Unicode 编码,现在很少使用了。 UTF-16 是 UTF-16,但通常只是被 Microsoft 和相关产品错误标记为“Unicode”。
  • 那是因为它在切换到 32 位代码点之前曾经被称为 Unicode。微软在标准制定之前就采用了它,并且很多功能和文档都有这个原始名称。
  • @codekaizen Unicode 被限制在 20 位多一点,它自己的向后兼容性要求阻止它超过这个大小,因为他们决定支持 UTF-16,它无法编码超过 0x10FFFF 的任何内容。支持这种原始 UTF-16 编码是 unicode 永远不会在 U+D800/U+DFFF 的 UTF-16 代理范围内分配字符的原因。为什么 unicode 会自愿选择限制自己免受未来增长的影响,这超出了我的理解,在我看来,这表明他们可能缺乏智慧。
猜你喜欢
  • 2014-11-20
  • 1970-01-01
  • 2010-09-13
  • 2013-10-17
  • 2011-05-30
  • 1970-01-01
  • 2023-03-03
  • 2012-12-19
相关资源
最近更新 更多