Введение

В данной статье сравниваются кодировки Unicode. Рассматриваются две ситуации: 8-битные среды без ограничений (которые можно считать типичными) и среды, запрещающие использование байтов со установленным старшим битом. Изначально такие запреты были введены для обеспечения совместимости со ссылками, использующими только семь бит данных, но они сохранились в некоторых стандартах, поэтому некоторые программные продукты, соответствующие этим стандартам, должны генерировать сообщения, соответствующие этим ограничениям. Стандартная схема сжатия для Unicode (Standard Compression Scheme for Unicode) и двоично-упорядоченное сжатие для Unicode (Binary Ordered Compression for Unicode) исключены из сравнительных таблиц, поскольку сложно однозначно оценить их размер.

Вопросы совместимости

Файл UTF-8, содержащий только символы ASCII, идентичен файлу ASCII. Устаревшие программы обычно могут обрабатывать файлы в кодировке UTF-8, даже если они содержат символы, не входящие в ASCII. Например, функция `printf` в C может печатать строку UTF-8, поскольку она ищет только символ ASCII '%' для определения строки форматирования и печатает все остальные байты без изменений, таким образом, символы, не входящие в ASCII, будут выводиться без изменений. UTF-16 и UTF-32 несовместимы с файлами ASCII и, следовательно, требуют программ, поддерживающих Unicode, для их отображения, печати и обработки, даже если известно, что файл содержит только символы из подмножества ASCII. Поскольку они содержат много нулевых байтов, строки не могут быть обработаны стандартными функциями для работы с null-terminated строками даже для простых операций, таких как копирование. Поэтому даже на большинстве систем UTF-16, таких как Windows и Java, текстовые файлы UTF-16 не распространены; вместо них используются старые 8-битные кодировки, такие как ASCII или ISO 8859-1, без поддержки Unicode, или UTF-8 используется для представления Unicode. Одним из редких исключений является файл "strings", используемый приложениями macOS (Mac OS X 10.3 Panther и более поздних версий) для поиска интернационализированных версий сообщений, который по умолчанию использует UTF-16, при этом "файлы, закодированные в UTF-8, могут работать некорректно". XML по соглашению кодируется в UTF-8, и все XML-процессоры должны поддерживать как минимум UTF-8 (включая US ASCII по определению) и UTF-16.

Эффективность

UTF 8 требует 8, 16, 24 или 32 бита (от одного до четырех байтов) для кодирования символа Unicode, UTF 16 требует 16 или 32 бита для кодирования символа, а UTF 32 всегда требует 32 бита для кодирования символа. Первые 128 кодовых точек Unicode, U+0000 до U+007F, используемые для управляющих символов C0 и основных латинских символов, и соответствующие один к одному их эквивалентам в коде ASCII, кодируются с использованием 8 бит в UTF 8, 16 бит в UTF 16 и 32 бит в UTF 32. Следующие 1920 символов, U+0080 до U+07FF (включающие в себя остальные символы почти всех латинских алфавитов, а также греческий, кириллицу, коптский, армянский, иврит, арабский, сирийский, тана и н’ко), требуют 16 бит для кодирования как в UTF 8, так и в UTF 16, и 32 бита в UTF 32. Для U+0800 до U+FFFF, то есть остальных символов в базовой многоязычной плоскости (BMP, плоскость 0, U+0000 до U+FFFF), которая включает в себя остальные символы большинства живых языков мира, UTF 8 требует 24 бита для кодирования символа, в то время как UTF 16 требует 16 бит и UTF 32 требует 32. Кодовые точки U+010000 - U+10FFFF, представляющие символы в дополнительных плоскостях (плоскости 1–16), требуют 32 бита в UTF 8, UTF 16 и UTF 32. Следовательно, файл в UTF 8 короче, чем в UTF 16, если количество кодовых точек ASCII больше, чем количество кодовых точек в диапазоне от U+0800 до U+FFFF. Удивительно, но документы реального мира, написанные на языках, использующих символы только в высоком диапазоне, зачастую все равно короче в UTF 8 из-за широкого использования пробелов, цифр, знаков препинания, переносов строк, HTML-разметки, а также встроенных слов и аббревиатур, написанных латинскими буквами. UTF 32 всегда длиннее, если только нет кодовых точек меньше U+10000. Все печатаемые символы в UTF EBCDIC используют не меньше байтов, чем в UTF 8, и большинство используют больше, из-за решения разрешить кодирование управляющих кодов C1 в виде отдельных байтов. Для семибитных сред UTF 7 более эффективен по объему, чем комбинация других кодировок Unicode с quoted printable или base64 для почти всех типов текста (см. раздел "Семибитные среды" ниже).

Время обработки

Текст с кодировкой переменной длины, такой как UTF-8 или UTF-16, сложнее обрабатывать, если требуется работа с отдельными кодовыми единицами, а не с последовательностями кодовых единиц. Поиск не зависит от переменного размера символов, поскольку поиск последовательности кодовых единиц не учитывает границы (хотя для этого требуется, чтобы кодировка была самосинхронизирующейся, что верно для UTF-8 и UTF-16). Распространенное заблуждение состоит в том, что необходимо "найти n-й символ", и для этого требуется кодировка фиксированной длины; однако на практике число n определяется только на основе анализа предыдущих n-1 символов, поэтому последовательный доступ к данным необходим в любом случае. При загрузке последовательностей символов в одном порядке байтов на машину с другим порядком байтов, символы необходимо преобразовать для эффективной обработки (либо требуются два процессора). Кодировки, основанные на байтах, такие как UTF-8, не имеют этой проблемы. UTF-16BE и UTF-32BE используют прямой порядок байтов (big-endian), UTF-16LE и UTF-32LE – обратный порядок байтов (little-endian).

Проблемы обработки

Для обработки формат должен быть удобным для поиска, усечения и, в целом, безопасной обработки. Все распространенные кодировки Unicode используют ту или иную форму кодовой единицы фиксированного размера. В зависимости от формата и кодовой точки, которую необходимо закодировать, одна или несколько таких кодовых единиц будут представлять кодовую точку Unicode. Для облегчения поиска и усечения последовательность не должна встречаться внутри более длинной последовательности или на границе двух других последовательностей. UTF-8, UTF-16, UTF-32 и UTF-EBCDIC обладают этими важными свойствами, в то время как UTF-7 и GB 18030 – нет. Символы фиксированного размера могут быть полезны, но даже если количество байт на кодовую точку фиксировано (как в UTF-32), количество байт на отображаемый символ не фиксировано из-за комбинируемых символов. Учитывая эти несовместимости и другие особенности различных схем кодирования, обработка Unicode-данных с использованием одного и того же (или совместимого) протокола на всех интерфейсах (например, при использовании API/библиотеки, обработке Unicode-символов в клиент-серверной модели и т.п.) в целом может упростить весь процесс и одновременно устранить потенциальный источник ошибок. UTF-16 популярен, поскольку многие API создавались во времена, когда Unicode имел фиксированную ширину 16 бит (известную как UCS-2). Однако использование UTF-16 делает символы вне базовой многоязычной плоскости особым случаем, что повышает риск ошибок при их обработке. Впрочем, программы, некорректно обрабатывающие суррогатные пары, вероятно, также испытывают проблемы с комбинируемыми последовательностями, поэтому использование UTF-32 вряд ли решит более общую проблему плохой обработки символов, состоящих из нескольких кодовых единиц. Если какие-либо хранимые данные представлены в UTF-8 (например, содержимое файлов или имена), очень сложно создать систему, использующую UTF-16 или UTF-32 в качестве API. Это связано с часто упускаемым из виду фактом, что байтовый массив, используемый UTF-8, физически может содержать недопустимые последовательности. Например, невозможно исправить недопустимое имя файла в UTF-8, используя UTF-16 API, поскольку ни одна возможная строка UTF-16 не будет соответствовать этому недопустимому имени файла. Обратное верно не всегда: тривиально преобразовать недопустимую строку UTF-16 в уникальную (хотя и технически недопустимую) строку UTF-8, поэтому UTF-8 API может управлять как UTF-8, так и UTF-16 файлами и именами, что делает UTF-8 предпочтительным в такой смешанной среде. К сожалению, гораздо более распространенным решением, используемым системами UTF-16, является интерпретация UTF-8 как другой кодировки, например CP 1252, и игнорирование "моджибаке" для любых данных, не относящихся к ASCII.

Для передачи и хранения

У UTF-16 и UTF-32 порядок байтов не определен, поэтому при получении данных по сети, ориентированной на байты, или при чтении из хранилища, ориентированного на байты, необходимо выбрать порядок байтов. Этого можно достичь, используя метку порядка байтов в начале текста или полагаясь на старший порядок байтов (RFC 2781). UTF-8, UTF-16BE, UTF-32BE, UTF-16LE и UTF-32LE стандартизированы для одного порядка байтов и не имеют этой проблемы. Если байтовый поток подвержен повреждениям, некоторые кодировки восстанавливаются лучше, чем другие. UTF-8 и UTF EBCDIC в этом отношении превосходят другие, поскольку они всегда могут повторно синхронизироваться после поврежденного или отсутствующего байта в начале следующего символа; GB 18030 не может восстановиться до следующего ASCII-символа, не являющегося числом. UTF-16 может обрабатывать измененные байты, но не нечетное количество недостающих байтов, что приведет к искажению всего последующего текста (хотя это и приведет к появлению необычных и/или неназначенных символов). Если биты могут быть потеряны, все они исказят последующий текст, хотя UTF-8 может быть повторно синхронизирован, поскольку неправильные границы байтов приведут к появлению недействительного UTF-8 почти во всем тексте длиной более нескольких байтов.

Подробно

В таблицах ниже приведено количество байт на кодовую точку для различных диапазонов Unicode. Любые необходимые дополнительные комментарии включены в таблицу. Приведенные цифры исходят из предположения, что служебные данные в начале и конце текстового блока пренебрежимо малы. Важно отметить: в таблицах ниже указано количество байт на кодовую точку, а не на видимый пользователю "символ" (или "кластер графем"). Для представления одного кластера графем может потребоваться несколько кодовых точек, поэтому даже при использовании UTF-32 необходимо соблюдать осторожность при разделении или объединении строк.

Восьмибитные среды

Кодовый диапазон (шестнадцатеричный) UTF 8 UTF 16 UTF 32 UTF EBCDIC GB 18030000000 – 00007F 12411000080 – 00009F 22 для символов, унаследованных от GB 2312/GBK (например, большинство китайских символов) 4 для всего остального. 0000A0 – 0003FF 2000400 – 0007FF 3000800 – 003FFF 3004000 – 00FFFF 4010000 – 03FFFF 444040000 – 10FFFF 5

Семибитные среды

Эта таблица может не охватывать каждый особый случай, поэтому ее следует использовать только для оценки и сравнения. Чтобы точно определить размер текста в кодировке, обратитесь к фактическим спецификациям. Кодовый диапазон (шестнадцатеричный) UTF 7UTF 8 quoted printableUTF 8 base64UTF 16 q. p. UTF 16 base64GB 18030 q. p. GB 18030 base64ASCIIграфические символы (кроме U+003D "=") 1 для "прямых символов" (зависит от настроек кодировщика для некоторых кодовых точек), 2 для U+002B "+", в противном случае – как для 000080 – 00FFFF11 1/342 2/311 1/300003D (знак равенства)363ASCIIконтрольные символы: 000000 – 00001F и 00007F1 или 3 в зависимости от того, является ли символ прямым1 или 3 в зависимости от того, является ли символ прямым000080 – 0007FF5 для изолированного случая внутри последовательности однобайтовых символов. Для последовательностей – 2 2/3 байта на символ плюс дополнение до целого числа байтов плюс два байта для начала и конца последовательности62 2/32–6 в зависимости от того, требуется ли экранирование байтовых значений4–6 для символов, унаследованных от GB2312/GBK (например, большинства китайских символов) 8 для всех остальных.2 2/3 для символов, унаследованных от GB2312/GBK (например, большинства китайских символов) 5 1/3 для всех остальных.000800 – 00FFFF94010000 – 10FFFF8 для изолированного случая, 5 1/3 байта на символ плюс дополнение до целого числа плюс 2 байта для последовательности125 1/38–12 в зависимости от того, требуется ли экранирование младших байтов суррогатных пар.5 1/385 1/3Порядок байтов не влияет на размер (UTF 16BE и UTF 32BE имеют тот же размер, что и UTF 16LE и UTF 32LE, соответственно). Использование UTF 32 в quoted printable крайне непрактично, но в случае реализации потребуется 8–12 байт на кодовую точку (в среднем около 10 байт), а именно для BMP каждая кодовая точка займет ровно на 6 байт больше, чем тот же код в quoted printable/UTF 16. Base64/UTF 32 использует 5 1/3 байта на любую кодовую точку. ASCII-символ управления в quoted printable или UTF 7 может быть представлен либо напрямую, либо закодированным (экранированным). Необходимость экранирования конкретного символа управления зависит от многих факторов, но символы новой строки в текстовых данных обычно кодируются напрямую.

Схемы сжатия

BOCU 1 и SCSU — это два способа сжатия данных Unicode. Их кодирование основано на частоте использования текста. Большинство последовательностей текста используют один и тот же шрифт, например, латинский, кириллицу, греческий и так далее. Такая типичная ситуация позволяет сжимать многие последовательности текста примерно до 1 байта на кодовую позицию. Эти кодировки, сохраняющие состояние, затрудняют произвольный доступ к тексту в любой позиции строки. Эти две схемы сжатия менее эффективны, чем другие схемы сжатия, такие как zip или bzip2. Эти универсальные схемы сжатия могут сжимать более длинные последовательности байтов всего до нескольких байтов. Схемы сжатия SCSU и BOCU 1 не позволяют сжать текст более чем на теоретические 25% по сравнению с текстом, закодированным в UTF-8, UTF-16 или UTF-32. Другие универсальные схемы сжатия могут легко сжимать текст до 10% от исходного размера. Универсальные схемы требуют более сложных алгоритмов и больших объемов текста для достижения хорошей степени сжатия. Более подробное сравнение схем сжатия содержится в технической заметке Unicode № 14.

Исторический: UTF-5 и UTF-6

Были сделаны предложения по использованию UTF-5 и UTF-6 для интернационализации доменных имен (IDN). В предложении UTF-5 использовалось кодирование с основанием 32, в то время как Punycode (среди прочего, и не совсем точно) использует кодирование с основанием 36. Название UTF-5 для кодовой единицы из 5 бит объясняется равенством 2⁵ = 32. Предложение UTF-6 добавляло кодирование длин серий к UTF-5, где цифра 6 просто означает UTF-5 плюс 1. Впоследствии рабочая группа IETF IDN приняла более эффективный Punycode для решения этой задачи.

Не будучи серьезно преследуемым

UTF-1 так и не получил широкого распространения. UTF-8 используется значительно чаще. Кодировки UTF-9 и UTF-18 — это шуточные спецификации RFC, приуроченные к Первому апреля, хотя UTF-9 является рабочим форматом преобразования Unicode, а UTF-18 — рабочая кодировка для всех кодовых точек, не входящих в частное использование в Unicode 12 и более ранних версиях, но не для дополнительных областей частного использования или частей Unicode 13 и более поздних версий.