А как насчёт скрестить UTF-8 и Win32 API?

Пришла давеча в голову совсем неоригинальная мысль – а почему ещё никто не сделал надстройку над Win32 API, позволяющую использовать строки, закодированные в UTF-8? Ведь существует же масса причин, чтобы такая библиотека пользовалась бы спросом, особенно несколько лет назад – во время массовой миграции с 9x на NT.

  1. Такую библиотеку несложно написать. Базовая инфраструктура в виде ANSI и Unicode вариантов одних и тех же Win32 функций существует. C и C++ поддерживают многобайтные символы (MBCS), в том числе и UTF-8.
  2. Перекодирование из UTF-8 в UTF-16 и обратно может показаться затратным, однако, к примеру, Wow64 делает фактически то же самое, перекодируя 32-х битные параметры в 64-х битные при каждом системном вызове и никакой особенной деградации производительности не заметно.
  3. Опыт применения UTF-8 в Unix мире показывает, что большинство ANSI приложений может без особых проблем работать с UTF-8 строками. Конечно, есть и отличия, но они легко обходятся.
  4. Переделка большого ANSI приложения в Unicode вариант, работающий с двухбайтовыми строками, весьма сложная и затратная процедура, требующая времени и немалого опыта от разработчиков. Перевод приложения на UTF-8. по всей видимости, должен быть значительно проще.

Конечно же, есть случаи, когда переход на UTF-8 сам по себе ничего не даёт, однако в большинстве ситуаций это вполне оправдано. Так почему же никто таким не пользуется?

PS: Беглый поиск в Интернете дал ссылку только одну ссылку UTF-8 Cygwin. Однако не удалось найти ни одной реализации для чистого Windows окружения.

,

  1. Александр
    June 4th, 2007 at 03:23 | #1

    А я уже сделал.
    Очень просто - создал клон класса CString (очень старый, от VC++ 4.0), выкинув из него двухбайтовость и левую многобайтность. Все хранение в однобайтном буфере (т.е. char *) строки UTF-8. Разумеется, конструкторы из CString и переопределенное присваивание.

    Я не стал переделывать системные вызовы. Везде, где надо, я в мои классы добавляю временную переменную типа CString, а в вызове делаю SomeStr.ToStr(cstr).
    Разумеется, это не так удобно, как обертывание всего API, но работает зашибись.
    К примеру, я смог использовать xgettext для интернационализации своих программ, что на порядок круче отстоя с интернационализацией ресурсов.
    Теперь языки у меня переключаются динамически прямо в запущенной программе, из меню.

    Производительность не пострадала - т.е. я не заметил тормозов. Хотя специально не замерял.

  2. кирилл
    June 4th, 2007 at 05:25 | #2

    Некоторые из стандартных Windows функций имеют UTF-8 overload-ы. Например - DNS функции. Загляните в список экспорта из DNSAPI.DLL и сможете увидеть например такое:
    DnsValidateName_A
    DnsValidateName_UTF8
    DnsValidateName_W

  3. June 4th, 2007 at 11:33 | #3

     

    Некоторые из стандартных Windows функций имеют UTF-8 overload-ы.

    Интересно, не знал об этом.

  4. Eugene Golushkov
    June 5th, 2007 at 06:46 | #4

    О поддержке UTF-8 в реальных проектах, которые рождались как ANSI великолепно написал Michael Caplan здесь http://blogs.msdn.com/michkap/archive/2007/05/11/2547703.aspx
    Он описывает нюансы, приведшие к прекращению поддержки UTF-8 в VCRT 8.0, и, насколько я понимаю, все доводы применимы и к MSLU.

    Цитата: But to be honest, given that every project I have ever seen that claimed to support UTF-8 failed in the 3/4 byte cases (some even failed in the 2-byte cases!)

    А еще нужно учесть, что длина UTF-8 представления символа из UCS-4 может быть до 6 байт (для code points за пределами первых 17 плоскостей, rfc2279).

  5. June 5th, 2007 at 21:37 | #5

    Не могу сказать что аргументы Майкла звучат убедительно. По моему, всё что он сказал это то, что проекты использующие UTF-16 поддерживают Unicode лучше чем UTF-8 аналоги только потому, что и там и там разработчики лажают с поддержкой многосимвольных последовательностей. Просто в один символ UTF-16 можно запихать гораздо больше разных кодировок, чем в в один символ UTF-8.

  6. Eugene Golushkov
    June 6th, 2007 at 13:48 | #6

    Сколько разных программистов, зная что в тексте ровно N байт выделяют для смены кодировки буфер размером 2*N , в неявном предположении, что при конвертации объем не может увеличиться больше чем в два раза? При этом число 2 скорее всего замаскировано чем-то вроде sizeof(wchar_t), если программист знает, что на Unix-ах wchar_t четырехбайтный. И большинство кодировок (все single-byte) вполне отвечают этому эмпирическому правилу, что позволяет таким ошибкам жить в коде, которому уже довольно много лет.

    Так что мы имеем два отрицательных момента. Первый состоит в том, что программисту нужно быть много аккуратнее в управлении памятью при добавлении поддержки UTF-8, так как максимальный размер символа может достигать 6-ти байт для UCS-4, 4-х байт для UTF-16 и 3-х байт для Basic Multilingual Plane. В сравнении с этим правила UTF-16 просты до безобразия - 2 байта для Basic Multilingual Plane и 4 байта для азиатских языков.

    Второй отрицательный момент в том, что для того, чтобы сделать что-либо полезное с текстом, будь то сортировка или отрисовка - внутри runtime (в широком смысле) все равно нужно будет получить code-point, что для UTF-8 более затратная операция.

    Ну и в качестве курьеза, который показывает, что за аргументацией Майкла Каплана возможно стоит нечто большее, чем абстрактные рассуждения. Google для запроса “utf-16 problem” находит 380 000 ссылок, а для “utf-8 problem” - 5 900 000. Разница более чем в 15 раз :)

  7. June 6th, 2007 at 22:04 | #7

    Да я согласная, согласная я. :-) За исключением ремарки про UCS-4. UTF-16 тоже не способна закодировать всё многообразие UCS-4.

    Моя мысль состоит в том, что при переделки ANSI проекта в Unicode в случае UTF-16 делается двойная работа - сначала все переводится на UCS-2, а потом, когда обнаруживается, что UCS-2 и UTF-16 это не одно и тоже, начинаются заделываться дырки с surrogate characters. И это если не затрагивать символов вроде ударений и прочих, имеющих культурно/лингвистическое значение.

    В случае же с UTF-8 массу усилий, которые в “нормальном” случае тратяться на героическую замену strlen на wstrlen (а по уму - сразу на StringCchLength), можно потратить на искоренение “лингвистисеких” проблем.

    В общем, нехорошо маскировать проблему и говорить, что мы, мол, поддерживаем весь Unicode, хотя на самом деле поддерживаются дай бог только европейские языки.

  1. June 3rd, 2007 at 17:20 | #1
  2. August 12th, 2007 at 22:40 | #2