Конвертер кодировок текста

UTF-32: четыре байта на каждый символ

Перекодировать текст из UTF-32 ↓

Представьте склад, на котором любую вещь, будь то иголка или рояль, упаковывают в одинаковые большие ящики. Места уходит много, зато найти нужный предмет проще простого: сотый по счёту товар лежит ровно в сотом ящике, и не нужно вскрывать предыдущие девяносто девять. По такому принципу устроена UTF-32, самая прямолинейная из всех кодировок Юникода. В ней нет хитрых правил, переменной длины и специальных случаев, а платой за простоту служит объём.

Правило у UTF-32 всего одно: каждый символ записывается тридцатидвухбитным числом, которое в точности равно его номеру в Юникоде. Латинская буква, русская буква, иероглиф и смайлик занимают одинаково по четыре байта. Никаких таблиц преобразования и вычислений не требуется, поэтому описание кодировки умещается в одну строку. Для сравнения: в UTF-8 тот же символ может занимать от одного до четырёх байтов, а в UTF-16 два или четыре, и программе приходится разбирать служебные биты, чтобы понять, где кончается один знак и начинается другой.

Предком UTF-32 была кодировка UCS-4 из международного стандарта ISO 10646. Она задумывалась с огромным запасом и теоретически допускала более двух миллиардов кодовых позиций. Позже создатели Юникода договорились, что реальный диапазон никогда не выйдет за пределы примерно миллиона ста тысяч значений, поскольку именно столько способна адресовать UTF-16. В 2001 году новая кодировка с этим ограничением была закреплена в стандарте под именем UTF-32. С практической точки зрения UCS-4 и UTF-32 сегодня означают одно и то же.

Из этого ограничения следует любопытный факт. Для записи любого номера в Юникоде достаточно двадцати одного бита, а значит, одиннадцать бит из каждых тридцати двух всегда равны нулю. Иначе говоря, около трети объёма любого текста в UTF-32 занимает заведомая пустота. Английская фраза, которая в ASCII или UTF-8 уместилась бы в сто байтов, здесь потребует четырёхсот. Русский текст окажется вдвое больше, чем в UTF-8 или UTF-16, и вчетверо больше, чем в однобайтовых Windows-1251 и KOI8-R.

Ради чего мириться с такой расточительностью? Ради мгновенного доступа. Если нужен символ с порядковым номером тысяча, достаточно умножить тысячу на четыре и сразу прочитать нужные байты. В кодировках с переменной длиной для этого пришлось бы просматривать строку с самого начала. Столь же просто вычисляется длина текста, выполняется обход в обратном направлении и вырезание фрагментов. Алгоритмы получаются короткими, и в них меньше места для ошибок.

Правда, это преимущество не так бесспорно, как кажется. То, что человек воспринимает как один знак, в Юникоде нередко складывается из нескольких кодовых позиций. Букву с ударением можно записать как букву и отдельный надстрочный значок, флаг страны состоит из двух символов-индикаторов, а изображение семьи собирается из нескольких человечков, склеенных невидимыми соединителями. Даже в UTF-32 разрез строки в произвольном месте способен оторвать ударение от буквы или разбить флаг на половинки. Фиксированная ширина избавляет от возни с байтами, но не отменяет необходимости понимать устройство текста.

Как и UTF-16, тридцатидвухбитная кодировка зависит от порядка байтов. Существуют варианты UTF-32BE и UTF-32LE, а для их различения в начало данных может помещаться метка порядка байтов. Ещё одна общая черта с UTF-16 состоит в несовместимости с ASCII: в обычном английском тексте три байта из каждых четырёх оказываются нулевыми. Старые программы и сетевые протоколы принимают нулевой байт за конец строки, поэтому передавать такой текст через них без специальной обработки нельзя.

По этим причинам UTF-32 почти не встречается в файлах и совсем не используется в интернете. Современные браузеры её попросту не поддерживают: веб-стандарты требуют UTF-8 и не предусматривают чтение страниц в четырёхбайтовом формате. Настоящая область применения UTF-32 находится внутри программ. В Linux и macOS именно тридцать два бита занимает тип "широкого символа" в языке C, в стандарте C++ для этой цели появился отдельный тип char32_t, а Python хранит строки четырьмя байтами на символ в тех случаях, когда в тексте есть знаки за пределами базовой плоскости Юникода.

Часто UTF-32 служит промежуточным звеном. Программа получает текст в UTF-8, разворачивает его в массив кодовых позиций, выполняет поиск, сравнение или подготовку к отрисовке шрифтом, а затем снова сворачивает результат в компактную форму для хранения или передачи. В таком сценарии лишний расход памяти носит временный характер и никого не беспокоит, зато обработка становится нагляднее.

Итог можно выразить коротко. UTF-8 выигрывает там, где важны компактность и совместимость, UTF-16 сохраняется в силу исторических причин внутри крупных платформ, а UTF-32 остаётся рабочим инструментом программиста. Обычному пользователю вряд ли доведётся получить документ в этой кодировке, но если такой файл всё же попался и открылся в виде букв, разделённых длинными промежутками, решение то же, что и всегда: преобразовать его в UTF-8.