ICU quick starter
Часто бывает необходимо выполнить преобразование текста из одной кодировки в другую. Скажем, отослать на HTTP сервер строку в Windows-1251, а после декодировать ответ.
WinAPI как всегда выручает - функции MultiByteToWideChar
и WideCharToMultiByte
делают преобразование из UTF-16 в необходимую кодировку и обратно. Список поддерживаемых кодовых страниц велик, но нет механизма для получения идентификатора кодировки по имени. Поэтому я задался целью найти альтернативу этим функциям. И я ее нашел - библиотека ICU. Есть еще iconv, но меня она оттолкнула всей этой гнушной чепухой, не захотелось даже возиться. А вот ICU вполне прилежно поставляется с проектными файлами для свежайшей версии Visual Studio.
Собрать ICU проще простого:
- Скачиваем архив с исходными кодами со странички проекта, под колонкой ICU4C;
- Разворачиваем содержимое и открываем
source\allinone\allinone.sln
; - Собираем обе конфигурации -
Debug
иRelease
; - Запускаем
bin\icuinfo.exe
, проверяем, чтоICU Initialization returned: U_ZERO_ERROR
; - Файл
bin\icudtXY.dll
должен быть внушительного размера - около 20 мегабайт.В случае получения ошибок
U_FILE_ACCESS_ERROR
илиU_MISSING_RESOURCE_ERROR
- проверяем размер файла. Если слишком маленький - пересобираем проектmakedata
, у меня один раз случилось подобное, долго не мог понять в чем дело. Нашел на сайте следующее:Why am I seeing a small ( only a few K ) instead of a large ( several megabytes ) data shared library (icudt)? Opening ICU services fails with U_MISSING_RESOURCE_ERROR and u_init() returns failure.
ICU libraries always must link with the ICU data library. However, so that ICU can bootstrap itself, it first builds a 'stub' data library, in icu\source\stubdata, so that the tools can function. You should only use this in production if you are NOT using DLL-mode data access, in which case you are accessing ICU data as individual files, as an archive (.dat) file, or some other means. Normally, you should be using the larger library built from icu\source\data. If you see this issue after ICU has completed building, re-run 'make' in icu\source\data, or the 'makedata' project in Visual Studio.
Размер bin\icudtXY.dll
можно уменьшить с помощью ICU Data Library Customizer. Файл icudt48l.dat
из скачанного архива необходимо раскрыть в папку source\data\in
(а может и source\data\out
, точно не помню) и пересобрать проект makedata
.
Рассмотрим пример использования этой замечательной библиотеки. Попытаемся получить строку Приветик, ICU!
в кодировке Windows 1251
, затем обратно в UTF-16
и сравнить с оригиналом:
#include "stdafx.h" #include <cassert> #include <unicode/ucnv.h> #include <unicode/ucsdet.h> int _tmain(int argc, _TCHAR* argv[]) { const wchar_t * const sourceUTF16 = L"Приветик, ICU!"; const size_t len = wcslen(sourceUTF16); UErrorCode uError(U_ZERO_ERROR); ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// UConverter * const pConverter = ucnv_open("windows-1251", &uError); if (U_FAILURE(uError)) { return -1; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// const char * const pszCharsetInternalName = ucnv_getName(pConverter, &uError); assert(U_SUCCESS(uError)); assert(pszCharsetInternalName); const char * const pszCharsetIANAName = ucnv_getStandardName(pszCharsetInternalName, "IANA", &uError); assert(U_SUCCESS(uError)); assert(pszCharsetIANAName); const size_t minCharSize = ucnv_getMinCharSize(pConverter); const size_t maxCharSize = ucnv_getMaxCharSize(pConverter); ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// const wchar_t *pSourceW = &sourceUTF16[0]; const wchar_t * const pSourceLimitW = &sourceUTF16[len]; char *targetEncoding = reinterpret_cast<char *>(_alloca(maxCharSize * (len + 1))); char *pTarget = &targetEncoding[0]; const char * const pTargetLimit = &targetEncoding[len]; ucnv_fromUnicode(pConverter, &pTarget, pTargetLimit, &pSourceW, pSourceLimitW, NULL, true, &uError); assert(U_SUCCESS(uError)); *pTarget = '\0'; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// const char *pSource = &targetEncoding[0]; const char * const pSourceLimit = &targetEncoding[len]; wchar_t * const targetUTF16 = reinterpret_cast<wchar_t *>(_alloca(sizeof(wchar_t) * (len + 1))); wchar_t *pTargetW = &targetUTF16[0]; const wchar_t * const pTargetLimitW = &targetUTF16[len]; ucnv_toUnicode(pConverter, &pTargetW, pTargetLimitW, &pSource, pSourceLimit, NULL, true, &uError); assert(U_SUCCESS(uError)); *pTargetW = L'\0'; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ucnv_close(pConverter); return 0; }
Разберем по порядку:
- Включение заголовочных файлов
unicode/ucnv.h
иunicode/ucsdet.h
из папкиinclude
; - Открытие конвертера функцией
ucnv_open
.В качестве параметра передаем имя кодовой страницы или ее аналог, один из многих альтернативных имен, поддерживаемых ICU.
- В отладочных целях получаем внутреннее и стандартное имена открытого конвертера функциями
ucnv_getName
иucnv_getStandardName
; - Основная часть - аллокация памяти под результат и использование конвертера по назначению (функции
ucnv_fromUnicode
иucnv_toUnicode
); - Освобождение ресурсов, закрытие конвертера функцией
ucnv_close
.
Линкеру понадобятся icuuc.lib
и icuin.lib
для успешного создания исполняемого файла. А ему, в свою очередь, - библиотеки icuuc48.dll
, icudt48.dll
и icuin48.dll
для успешного запуска.
В результате выполнения получаем следующий результат:
1 коммент.:
wonderful post, very informative. I'm wondering why the opposite specialists of this sector do not realize this. You should proceed your writing. I'm confident, you've a great readers' base already!
Here is my homepage: free online backup service
my webpage - unlimited online backup
Отправить комментарий