Oakley Polarized Juliet
Заказал себе Oakley Polarized Juliet. 400$ в США, без примерки, естественно. Я идиот?
Заказал себе Oakley Polarized Juliet. 400$ в США, без примерки, естественно. Я идиот?
Часто бывает необходимо выполнить преобразование текста из одной кодировки в другую. Скажем, отослать на HTTP сервер строку в Windows-1251, а после декодировать ответ.
WinAPI как всегда выручает - функции MultiByteToWideChar
и WideCharToMultiByte
делают преобразование из UTF-16 в необходимую кодировку и обратно. Список поддерживаемых кодовых страниц велик, но нет механизма для получения идентификатора кодировки по имени. Поэтому я задался целью найти альтернативу этим функциям. И я ее нашел - библиотека ICU. Есть еще iconv, но меня она оттолкнула всей этой гнушной чепухой, не захотелось даже возиться. А вот ICU вполне прилежно поставляется с проектными файлами для свежайшей версии Visual Studio.
Собрать ICU проще простого:
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
для успешного запуска.
В результате выполнения получаем следующий результат: