Вот моя ситуация: мне нужно правильно определить, какая кодировка символов используется для данного текстового файла. Надеюсь, он может правильно вернуть один из следующих типов:

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

До сих пор я могу правильно определить текстовый файл как Unicode, Unicode big endian или UTF-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;
}

Эта проблема меня давно блокирует, и я до сих пор не могу найти хорошее решение. Любой намек будет оценен по достоинству.

8
herohuyongtao 23 Дек 2013 в 19:47
6
Термин «ANSI» часто неправильно используется для обозначения 8-битной кодировки, обычно одной из специфичных для Windows, такой как Windows-1252, которая так и не стала стандартом ANSI. Термин «Unicode» часто неправильно используется в мире Microsoft для обозначения кодировки UTF-16; Юникод не является кодировкой, но есть несколько кодировок, которые можно использовать для представления Юникода. Файл ASCII неотличим от файла UTF-8, который не содержит символов вне диапазона 0..127. Большинство файлов UTF-8 не начинаются с спецификации (поскольку UTF-8 не имеет порядка байтов).
 – 
Keith Thompson
23 Дек 2013 в 20:39
1
Вместо того, чтобы перечислять типы кодировки в комментарии, перечислите их в enum.
 – 
Casey
24 Дек 2013 в 00:33

1 ответ

Лучший ответ

Во-первых, нет такой физической кодировки, как «Юникод». Вы, вероятно, имеете в виду UTF-16. Во-вторых, любой файл допустим в "ANSI" или любой однобайтовой кодировке, если на то пошло. Единственное, что вы можете сделать, - это угадать в наилучшем порядке, который с наибольшей вероятностью выбрасывает недопустимые совпадения.

Вы должны проверить в следующем порядке:

  • Есть ли в начале спецификация UTF-16? Тогда это, наверное, UTF-16. Используйте спецификацию в качестве индикатора, является ли она прямым или прямым порядком байтов, а затем проверьте остальную часть файла, соответствует ли она.
  • Есть ли в начале спецификация UTF-8? Тогда это, наверное, UTF-8. Проверьте остальную часть файла.
  • Если вышеуказанное не привело к положительному совпадению, проверьте, соответствует ли весь файл UTF-8. Если это так, вероятно, это UTF-8.
  • Если вышеуказанное не привело к положительному совпадению, вероятно, это ANSI.

Если вы ожидаете, что файлы UTF-16 без спецификации также будут (это возможно, например, для файлов XML, которые указывают кодировку в объявлении XML), то вам также необходимо вставить это правило туда. Хотя любое из вышеперечисленного может привести к ложному срабатыванию, ошибочно идентифицируя файл ANSI как UTF- * (хотя это маловероятно ). У вас всегда должны быть метаданные , которые сообщают вам, в какой кодировке находится файл, обнаружение их постфактум невозможно со 100% точностью.

7
deceze 23 Дек 2013 в 20:08
Я только что заметил, что в Notepad ++ нет UTF-16. Вместо этого у него есть еще два типа: UCS-2 Big Endian и UCS-2 Little Endian. Итак, UTF-16 здесь эквивалентен UCS-2?
 – 
herohuyongtao
23 Дек 2013 в 20:18
1
Нет, UCS-2 - это более старая кодировка Unicode, которая больше не используется. UTF-16 - это UTF-16, но обычно Microsoft и сопутствующие продукты ошибочно маркируют его как «Unicode».
 – 
deceze
23 Дек 2013 в 20:25
1
Это потому, что раньше он назывался Unicode, прежде чем переключился на 32-битные кодовые точки. Microsoft приняла его до того, как был установлен стандарт, и многие функции и документация носят это первоначальное название.
 – 
codekaizen
13 Дек 2017 в 23:06
Unicode ограничен немногим более 20 бит, его собственные требования обратной совместимости не позволяют ему увеличиваться до этого размера, потому что они решили поддерживать UTF-16, который не может кодировать что-либо более 0x10FFFF. Поддержка этой примитивной кодировки UTF-16 является причиной того, почему Unicode никогда не будет назначать символ в суррогатном диапазоне UTF-16 U + D800 / U + DFFF. ПОЧЕМУ Юникод добровольно решил ограничить себя от будущего роста, мне непонятно, и, по моему мнению, это указывает на вероятную нехватку интеллекта с их стороны.
 – 
user3338098
12 Ноя 2020 в 02:08