Hungry Mind , Blog about everything in IT - C#, Java, C++, .NET, Windows, WinAPI, ...

Oakley Polarized Juliet

Заказал себе Oakley Polarized Juliet. 400$ в США, без примерки, естественно. Я идиот?

Вот так нужно с тупыми бабами за рулем!


ICU quick starter

Часто бывает необходимо выполнить преобразование текста из одной кодировки в другую. Скажем, отослать на HTTP сервер строку в Windows-1251, а после декодировать ответ.

WinAPI как всегда выручает - функции MultiByteToWideChar и WideCharToMultiByte делают преобразование из UTF-16 в необходимую кодировку и обратно. Список поддерживаемых кодовых страниц велик, но нет механизма для получения идентификатора кодировки по имени. Поэтому я задался целью найти альтернативу этим функциям. И я ее нашел - библиотека ICU. Есть еще iconv, но меня она оттолкнула всей этой гнушной чепухой, не захотелось даже возиться. А вот ICU вполне прилежно поставляется с проектными файлами для свежайшей версии Visual Studio.

Собрать ICU проще простого:

  1. Скачиваем архив с исходными кодами со странички проекта, под колонкой ICU4C;
  2. Разворачиваем содержимое и открываем source\allinone\allinone.sln;
  3. Собираем обе конфигурации - Debug и Release;
  4. Запускаем bin\icuinfo.exe, проверяем, что ICU Initialization returned: U_ZERO_ERROR;
  5. Файл 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;
}

Разберем по порядку:

  1. Включение заголовочных файлов unicode/ucnv.h и unicode/ucsdet.h из папки include;
  2. Открытие конвертера функцией ucnv_open.

    В качестве параметра передаем имя кодовой страницы или ее аналог, один из многих альтернативных имен, поддерживаемых ICU.

  3. В отладочных целях получаем внутреннее и стандартное имена открытого конвертера функциями ucnv_getName и ucnv_getStandardName;
  4. Основная часть - аллокация памяти под результат и использование конвертера по назначению (функции ucnv_fromUnicode и ucnv_toUnicode);
  5. Освобождение ресурсов, закрытие конвертера функцией ucnv_close.

Линкеру понадобятся icuuc.lib и icuin.lib для успешного создания исполняемого файла. А ему, в свою очередь, - библиотеки icuuc48.dll, icudt48.dll и icuin48.dll для успешного запуска.

В результате выполнения получаем следующий результат:

ICU proof of concept
Copyright 2007-2011 Chabster