Страницы

Поиск по вопросам

четверг, 12 декабря 2019 г.

Как отличить текст в файле с обычной кодировкой от Unicode?

#cpp #windows #unicode #текст


Нужно прочесть текстовый файл. Как узнать, закодирован таблицей символов (однобайтовых),
или в файле содержится текст в формате Unicode (16-ти битные символы)?
    


Ответы

Ответ 1



Файл всегда содержит байты. Иногда содержимое файла можно декодировать в текст, используя выбранную кодировку такую как cp1251, cp866, utf-8, или utf-16le. На Windows, файлы, закодированные в utf-16, к сожалению иногда называют Unicode (что вводит в заблуждение: Unicode—это не кодировка). utf-16 это всего лишь одна из многих кодировок, которую можно использовать, чтобы закодировать текст (Unicode) в байты: байты = юникод_текст.encode(кодировка) юникод_текст = байты.decode(кодировка) Файлы, содержащие текст, закодированный в utf-8, utf-16, utf-32 и других кодировках, могут содержать в начале специальную последовательность байт (U+FEFF символ BOM, закодированный в соответствующей кодировке), которая идентифицирует эти кодировки. Если файл следует этому соглашению, то достаточно несколько первых байт из файла (в двоичном режиме открытого) сравнить с вариантами BOM, чтобы определить соответствующую кодировку. В общем случае нет гарантированного на 100% способа определить кодировку файла (хотя некоторые кодировки могут быть более вероятны чем другие и может быть API, которое пытается угадать кодировку, такое как: IsTextUnicode() с IS_TEXT_UNICODE_STATISTICS). Пример: "Bush hid the facts" текст, закодированный в ascii кодировке, мог некоторыми приложениями интерпретироваться как текст в utf-16le кодировке, приводя к кракозябрам.

Ответ 2



Зависит от соглашения, в котором записан текстовый файл. Текстовый поток, записанный в формате Unicode может начитаться с BOM - Byte Order Mark, т.е. например с магических байтов FF, FE для UTF-16 Little Endian. А в отсутствие каких-либо соглашений - только анализ текста с элементами гадания на кофейной гуще.

Ответ 3



Простая на первый взгляд задача, но выполнить её оказывается не просто. С++ обладает достаточной гибкостью, как язык среднего уровня, поэтому требуется исчерпывающее знание вопроса для эффективной реализации задачи. Поделюсь тем что выяснил.Чтобы правильно прочесть файл нужно сначала посмотреть, есть ли в начале файла Маркер последовательности (тут точнее). Если есть маркер, его нужно определить. Если маркера нет, как выше уже было сказано, нужно искать другой алгоритм определения кодировки.Я тут целиком и полностью полагаюсь на IsTextUnicode, если маркера нет:BYTE *pBuf;size_t szRead, szBOM; // к-во прочитанных в файле байт и к-во байт маркераenum Unicode eUnicode;LPTSTR pszText;...// определяю, есть ли в тексте маркер (ручная работа)szBOM = IsUnicodeRaw(pBuf, szRead, &eUnicode);// вызов библиотечной ф-ции IsTextUnicode после собственной проверкиif(eUnicode != utf_16LE && ((szRead - szBOM) % 2 || !IsTextUnicode(pBuf + szBOM, (int) (szRead - szBOM), NULL))){ BYTE *pb = new BYTE[(szRead - szBOM + 1) * sizeof(wchar_t)]; unsigned int nCP = (eUnicode == utf_8) ? CP_UTF8 : (eUnicode == utf_7) ? CP_UTF7 : CP_ACP; if (!MultiByteToWideChar(nCP, (nCP == CP_ACP) ? MB_PRECOMPOSED : 0, (LPCSTR) (pBuf + szBOM), (int) (szRead - szBOM), (LPWSTR) pb, (int) (szRead - szBOM))) { // ошибка... } delete[] pBuf; pszText = (LPTSTR) (pszBuf = pb)}else pszText = (LPTSTR) (pszBuf + szBOM);После того как выяснил наличие маркера в ф-ции IsUnicodeRaw, обращаюсь к IsTextUnicode без маркера. Работает с UTF-8, с UTF-7 нужно разбираться - там последние 2 бита маркера являются частью следующего за маркером символа. ANSI-текст так же нормально кодирует в двухбайтовый.Wind'а предпочитает UTF-8 и UTF-16LE. Перед тем как записать текст, его нужно либо перекодировать в ANSI ф-цией WideCharToMultibyte, либо в начало файла записать маркер кодировки UTF-8 или UTF-16LE, чтобы потом также прочесть.

Ответ 4



Кажется, нашёл. Ф-ция IsTextUnicode из Advapi32.dll ответит на поставленный вопрос.

Комментариев нет:

Отправить комментарий