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

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

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

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

Copyright 2007-2011 Chabster