如何正确确定文本文件的字符编码

How to correctly determine character encoding of text files?

本文关键字:字符 编码 文件 文本 何正确      更新时间:2023-10-16

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

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

到目前为止,我可以通过调用以下函数正确地判断文本文件是UnicodeUnicode big endianUTF-8 with BOM。它还可以正确地为ANSI确定给定的文本文件最初是否不是UTF-8 without BOM问题是当文本文件是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;
}

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

对于初学者来说,没有"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%准确地在事后检测到它。