Стандарт | Стандарт Юникода |
---|---|
Классификация | Формат преобразования Unicode , расширенный ASCII , кодировка переменной длины |
Расширяет | ASCII |
Преобразует/Кодирует | ISO/IEC 10646 ( Юникод ) |
Предшествовал | UTF-1 |
UTF-8 — это стандарт кодировки символов , используемый для электронной связи. Определенный стандартом Unicode , название происходит от Unicode Transformation Format – 8-bit . [1] Почти каждая веб-страница хранится в UTF-8.
UTF-8 способен кодировать все 1 112 064 [2] допустимых кодовых точек Unicode, используя кодировку переменной ширины от одного до четырех однобайтовых ( 8-битовых) кодовых единиц. Кодовые точки с более низкими числовыми значениями, которые, как правило, встречаются чаще, кодируются с использованием меньшего количества байтов. Он был разработан для обратной совместимости с ASCII : первые 128 символов Unicode, которые соответствуют ASCII один к одному, кодируются с использованием одного байта с тем же двоичным значением, что и ASCII, так что файл в кодировке UTF-8, использующий только эти символы, идентичен файлу ASCII. Большинство программного обеспечения, разработанного для любого расширенного ASCII, может читать и записывать UTF-8 (включая Microsoft Windows ), и это приводит к меньшему количеству проблем с интернационализацией, чем любая альтернативная текстовая кодировка. [3] [4]
Международная организация по стандартизации (ISO) приступила к составлению универсального многобайтового набора символов в 1989 году. Проект стандарта ISO 10646 содержал необязательное приложение под названием UTF-1 , которое обеспечивало кодирование потока байтов его 32-битных кодовых точек. Эта кодировка была неудовлетворительной с точки зрения производительности, среди прочих проблем, и самой большой проблемой, вероятно, было то, что в ней не было четкого разделения между ASCII и не-ASCII: новые инструменты UTF-1 были бы обратно совместимы с текстом в кодировке ASCII, но текст в кодировке UTF-1 мог бы сбить с толку существующий код, ожидающий ASCII (или расширенный ASCII ), поскольку он мог содержать байты продолжения в диапазоне 0x21–0x7E, которые означали что-то другое в ASCII, например, 0x2F для /
, разделителя каталогов пути Unix .
В июле 1992 года комитет X/Open XoJIG искал лучшую кодировку. Дэйв Проссер из Unix System Laboratories представил предложение о кодировке, которая имела бы более быстрые характеристики реализации, и ввел улучшение, заключающееся в том, что 7-битные символы ASCII будут представлять только себя; многобайтовые последовательности будут включать только байты с установленным старшим битом. Название File System Safe UCS Transformation Format ( FSS-UTF ) [5] и большая часть текста этого предложения были позже сохранены в окончательной спецификации. [6] [7] [8] В августе 1992 года это предложение было распространено представителем IBM X/Open среди заинтересованных сторон. Модификация Кена Томпсона из группы операционной системы Plan 9 в Bell Labs сделала его самосинхронизирующимся , позволяя читателю начинать в любом месте и немедленно определять границы символов, за счет несколько меньшей битовой эффективности, чем предыдущее предложение. Оно также отказалось от использования смещений, которые предотвращали слишком длинные кодировки. [8] [9] Проект Томпсона был представлен 2 сентября 1992 года на подставке в закусочной в Нью-Джерси вместе с Робом Пайком . В последующие дни Пайк и Томпсон реализовали его и обновили Plan 9 , чтобы использовать его повсюду, [10] а затем сообщили о своем успехе обратно в X/Open, которая приняла его в качестве спецификации для FSS-UTF. [8]
UTF-8 был впервые официально представлен на конференции USENIX в Сан-Диего , проходившей с 25 по 29 января 1993 года. [11] Целевая группа по инжинирингу Интернета приняла UTF-8 в своей Политике по наборам символов и языкам в RFC 2277 ( BCP 18) для будущей работы над стандартами Интернета в январе 1998 года, заменив однобайтовые наборы символов, такие как Latin-1 в старых RFC. [12]
В ноябре 2003 года UTF-8 был ограничен RFC 3629 для соответствия ограничениям кодировки символов UTF-16 : явный запрет кодовых точек, соответствующих высоким и низким суррогатным символам, удалил более 3% трехбайтовых последовательностей, а окончание на U+10FFFF удалило более 48% четырехбайтовых последовательностей и все пяти- и шестибайтовые последовательности. [13]
UTF-8 кодирует кодовые точки в одном-четырех байтах, в зависимости от значения кодовой точки. В следующей таблице символы от u до z заменяются битами кодовой точки, начиная с позиций U+uvwxyz :
Первая кодовая точка | Последняя кодовая точка | Байт 1 | Байт 2 | Байт 3 | Байт 4 |
---|---|---|---|---|---|
У+0000 | U+007F | 0yyyzzzz | |||
U+0080 | У+07FF | 110xxxyy | 10yyzzzz | ||
U+0800 | У+ФФФФ | 1110wwww | 10xxxxyy | 10yyzzzz | |
U+010000 | У+10ФФФФ | 11110uvv | 10vvwwww | 10xxxxyy | 10yyzzzz |
Для первых 128 кодовых точек (ASCII) требуется 1 байт. Для кодирования следующих 1920 кодовых точек требуется два байта, что охватывает оставшуюся часть почти всех алфавитов с латинским алфавитом , а также расширения IPA , греческий , кириллический , коптский , армянский , иврит , арабский , сирийский , алфавиты тхана и нко , а также комбинированные диакритические знаки . Три байта необходимы для оставшихся 61 440 кодовых точек базовой многоязычной плоскости (BMP), включая большинство китайских, японских и корейских символов . Четыре байта необходимы для 1 048 576 кодовых точек в других плоскостях Unicode , которые включают эмодзи (пиктографические символы), менее распространенные символы CJK , различные исторические письменности и математические символы .
Это префиксный код , и для его декодирования не нужно читать после последнего байта кодовой точки. В отличие от многих более ранних многобайтовых текстовых кодировок, таких как Shift-JIS , он самосинхронизируется , поэтому возможен поиск коротких строк или символов, а начало кодовой точки можно найти из случайной позиции, отступив максимум на 3 байта. Значения, выбранные для ведущих байтов, означают, что сортировка списка строк UTF-8 помещает их в тот же порядок, что и сортировка строк UTF-32 .
Использование строки в таблице выше для кодирования кодовой точки, меньшей, чем «Первая кодовая точка» (таким образом, используя больше байтов, чем необходимо), называется сверхдлинной кодировкой . Это проблема безопасности, поскольку она позволяет кодировать одну и ту же кодовую точку несколькими способами. Сверхдлинные кодировки (например ../
) использовались для обхода проверок безопасности в высококлассных продуктах, включая веб-сервер IIS от Microsoft [14] и контейнер сервлетов Tomcat от Apache. [15] Поэтому сверхдлинные кодировки следует считать ошибкой и никогда не декодировать. Модифицированный UTF-8 допускает сверхдлинную кодировку U+0000 .
В приведенной ниже таблице дано подробное значение каждого байта в потоке, закодированном в UTF-8.
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | А | Б | С | Д | Э | Ф | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | ␀ | ␁ | ␂ | ␃ | ␄ | ␅ | ␆ | ␇ | ␈ | ␉ | ␊ | ␋ | ␌ | ␍ | ␎ | ␏ |
1 | ␐ | ␑ | ␒ | ␓ | ␔ | ␕ | ␖ | ␗ | ␘ | ␙ | ␚ | ␛ | ␜ | ␝ | ␞ | ␟ |
2 | ␠ | ! | " | # | $ | % | & | ' | ( | ) | * | + | , | - | . | / |
3 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | : | ; | < | = | > | ? |
4 | @ | А | Б | С | Д | Э | Ф | Г | ЧАС | я | Дж. | К | Л | М | Н | О |
5 | П | В | Р | С | Т | У | В | Вт | Х | И | З | [ | \ | ] | ^ | _ |
6 | ` | а | б | с | г | е | ф | г | час | я | дж | к | л | м | н | о |
7 | п | д | г | с | т | ты | в | ж | х | у | з | { | | | } | ~ | ␡ |
8 | ||||||||||||||||
9 | ||||||||||||||||
А | ||||||||||||||||
Б | ||||||||||||||||
С | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 |
Д | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 |
Э | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 |
Ф | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 5 | 5 | 5 | 5 | 6 | 6 |
Управляющий символ ASCII | |
ASCII-символ | |
Продолжение байта | |
Первый байт 2-байтовой кодовой точки | |
Первый байт 3-байтовой кодовой точки | |
Первый байт 4-байтовой кодовой точки | |
Неиспользованный |
Не все последовательности байтов являются допустимыми UTF-8. Декодер UTF-8 должен быть подготовлен для:
Многие из первых декодеров UTF-8 декодировали бы их, игнорируя неправильные биты. Тщательно созданный недействительный UTF-8 мог бы заставить их либо пропускать, либо создавать символы ASCII, такие как NUL , косая черта или кавычки, что приводило бы к уязвимостям безопасности. Также распространено выдавать исключение или обрезать строку при ошибке [16], но это превращает то, что в противном случае было бы безобидными ошибками (например, «файл не найден»), в отказ в обслуживании , например, ранние версии Python 3.0 немедленно завершали работу, если командная строка или переменные среды содержали недействительный UTF-8. [17]
RFC 3629 гласит: «Реализации алгоритма декодирования ДОЛЖНЫ защищать от декодирования недействительных последовательностей». [18] Стандарт Unicode требует от декодеров: «... рассматривать любую неправильно сформированную последовательность кодовых единиц как состояние ошибки. Это гарантирует, что он не будет ни интерпретировать, ни выдавать неправильно сформированную последовательность кодовых единиц». Теперь стандарт рекомендует заменять каждую ошибку заменяющим символом «�» (U+FFFD) и продолжать декодирование.
Некоторые декодеры считают последовательность E1,A0,20 (усеченный 3-байтовый код, за которым следует пробел) одной ошибкой. Это не очень хорошая идея, так как поиск символа пробела найдет тот, который скрыт в ошибке. Начиная с Unicode 6 (октябрь 2010 г.) [19] стандарт (глава 3) рекомендовал «лучшую практику», когда ошибка представляет собой либо один байт продолжения, либо заканчивается на первом байте, который запрещен, поэтому E1,A0,20 является двухбайтовой ошибкой, за которой следует пробел. Это означает, что ошибка имеет длину не более трех байтов и никогда не содержит начало допустимого символа, и есть21 952 различных возможных ошибок. Технически это делает UTF-8 больше не префиксным кодом (вам нужно прочитать один байт после некоторых ошибок, чтобы понять, что это ошибка), но поиск все еще работает, если искомая строка не содержит ошибок.
Если сделать каждый байт ошибкой, в этом случае E1,A0,20 — это две ошибки, за которыми следует пробел, это также позволяет искать допустимую строку. Это означает, что существует всего 128 различных ошибок, что делает практичным сохранение ошибок в выходной строке [20] или замену их символами из устаревшей кодировки.
Только небольшое подмножество возможных строк байтов являются безошибочными UTF-8: несколько байтов не могут появляться; байт с установленным старшим битом не может быть один; и в действительно случайной строке байт с установленным старшим битом имеет только 1 ⁄ 15 шанс начать допустимый символ UTF-8. Это имеет (возможно, непреднамеренное) последствие, заключающееся в том, что становится легко обнаружить, если устаревшая текстовая кодировка случайно используется вместо UTF-8, что упрощает преобразование системы в UTF-8 и устраняет необходимость требовать Byte Order Mark или любые другие метаданные.
Начиная с RFC 3629 (ноябрь 2003 г.), высокие и низкие суррогаты, используемые UTF-16 ( от U+D800 до U+DFFF ), не являются допустимыми значениями Unicode, и их кодировки UTF-8 должны рассматриваться как недопустимая последовательность байтов. [18] Все эти кодировки начинаются с 0xED, за которым следует 0xA0 или выше. Это правило часто игнорируется, поскольку суррогаты разрешены в именах файлов Windows, и это означает, что должен быть способ сохранить их в строке. [21] UTF-8, который допускает эти суррогатные половины, (неофициально) называется WTF-8 , [22] в то время как другая вариация, которая также кодирует все не-BMP символы как два суррогата (6 байт вместо 4), называется CESU-8 .
Если метка порядка байтов Unicode U+FEFF находится в начале файла UTF-8, первые три байта будут 0xEF , 0xBB , 0xBF .
Стандарт Unicode не требует и не рекомендует использовать BOM для UTF-8, но предупреждает, что он может встретиться в начале файла, транскодированного из другой кодировки. [23] Хотя текст ASCII, закодированный с использованием UTF-8, обратно совместим с ASCII, это неверно, когда рекомендации стандарта Unicode игнорируются и добавляется BOM. BOM может сбить с толку программное обеспечение, которое не подготовлено к нему, но в противном случае может принять UTF-8, например языки программирования, которые разрешают не-ASCII байты в строковых литералах , но не в начале файла. Тем не менее, было и все еще есть программное обеспечение, которое всегда вставляет BOM при записи UTF-8 и отказывается правильно интерпретировать UTF-8, если первый символ не является BOM (или файл содержит только ASCII). [24]
Долгое время велись жаркие споры о том, какой формат лучше — UTF-16 или UTF-8.
Основным преимуществом UTF-16 является то, что Windows API требовал его использования для доступа ко всем символам Unicode (только недавно это было исправлено). Это привело к тому, что несколько библиотек, таких как Qt, также стали использовать строки UTF-16, что распространяет это требование на платформы, отличные от Windows.
В ранние дни Unicode не было символов больше U+FFFF , а комбинированные символы использовались редко, поэтому 16-битная кодировка имела фиксированный размер. Это сделало обработку текста более эффективной, хотя выигрыши не так велики, как могут себе представить начинающие программисты. Все эти преимущества были утрачены, как только UTF-16 стал также переменной ширины.
Кодовые точки U+0800 – U+FFFF занимают 3 байта в UTF-8, но только 2 в UTF-16. Это привело к идее, что текст на китайском и других языках будет занимать больше места в UTF-8. Однако текст становится больше только в том случае, если этих кодовых точек больше, чем 1-байтовых кодовых точек ASCII, и это редко случается в реальных документах из-за пробелов, новых строк, цифр, знаков препинания, английских слов и разметки HTML.
Преимуществом UTF-8 является то, что его легко модернизировать для любой системы, способной обрабатывать расширенный ASCII , не возникает проблем с порядком байтов, и он занимает примерно половину пространства для любого языка, использующего в основном латинские буквы.
UTF-8 является наиболее распространенной кодировкой для Всемирной паутины с 2008 года. [26] По состоянию на октябрь 2024 года [обновлять]UTF-8 используется на 98,3% обследованных веб-сайтов. [27] Хотя многие страницы используют только символы ASCII для отображения контента, очень немногие веб-сайты теперь заявляют, что их кодировка — только ASCII, а не UTF-8. [28] Более 50% отслеживаемых языков используют 100% UTF-8.
Многие стандарты поддерживают только UTF-8, например, обмен JSON требует этого (без метки порядка байтов (BOM)). [29] UTF-8 также является рекомендацией WHATWG для спецификаций HTML и DOM , и заявляет, что «кодировка UTF-8 является наиболее подходящей кодировкой для обмена Unicode » [4] , а Консорциум электронной почты Интернета рекомендует, чтобы все программы электронной почты могли отображать и создавать почту с использованием UTF-8. [30] [31] Консорциум Всемирной паутины рекомендует UTF-8 в качестве кодировки по умолчанию в XML и HTML (и не просто использовать UTF-8, а также объявлять ее в метаданных), «даже если все символы находятся в диапазоне ASCII... Использование кодировок, отличных от UTF-8, может иметь неожиданные результаты». [32]
Множество программных продуктов имеют возможность читать/писать UTF-8. Однако пользователю может потребоваться изменить параметры обычных настроек или может потребоваться BOM (метка порядка байтов) в качестве первого символа для чтения файла. Примерами программных продуктов, поддерживающих UTF-8, являются Microsoft Word , [33] [34] [35] Microsoft Excel (2016 и более поздние версии), [36] [37] Google Drive , LibreOffice и большинство баз данных.
Программное обеспечение, которое «по умолчанию» использует UTF-8 (то есть записывает без изменения настроек пользователем и считывает без BOM), стало более распространенным с 2010 года. [38] Блокнот Windows во всех поддерживаемых в настоящее время версиях Windows по умолчанию записывает UTF-8 без BOM (изменение по сравнению с Блокнотом Windows 7 ), что приводит его в соответствие с большинством других текстовых редакторов. [39] Некоторые системные файлы в Windows 11 требуют UTF-8 [40] без необходимости в BOM, и почти все файлы в macOS и Linux должны быть в UTF-8 без BOM. [ необходима цитата ] Языки программирования, которые по умолчанию используют UTF-8 для ввода-вывода, включают Ruby 3.0, [41] [42] R 4.2.2, [43] Raku и Java 18. [44] Хотя текущая версия Python требует опции для чтения/записи UTF-8, [45] существуют планы сделать ввод-вывод UTF-8 по умолчанию в Python 3.15. [46] C++23 принимает UTF-8 как единственный переносимый формат файла исходного кода (удивительно, что раньше его не было). [47] open()
Обратная совместимость является серьезным препятствием для изменения кода и API, использующих UTF-16 , на использование UTF-8, но это происходит. По состоянию на май 2019 года [обновлять]Microsoft добавила возможность для приложения устанавливать UTF-8 в качестве «кодовой страницы» для API Windows, устраняя необходимость использования UTF-16; и совсем недавно рекомендовала программистам использовать UTF-8, [48] и даже заявила, что «UTF-16 [...] — это уникальная нагрузка, которую Windows возлагает на код, предназначенный для нескольких платформ». [3] Примитив строки по умолчанию в Go , [49] Julia , Rust , Swift (начиная с версии 5), [50] и PyPy [51] использует UTF-8 внутренне во всех случаях. Python (начиная с версии 3.3) использует UTF-8 внутренне для расширений Python C API [52] [53] и иногда для строк [52] [54] , а в будущей версии Python планируется хранить строки как UTF-8 по умолчанию. [55] [56] Современные версии Microsoft Visual Studio используют UTF-8 внутри. [57] В Microsoft SQL Server 2019 добавлена поддержка UTF-8, и ее использование приводит к увеличению скорости на 35% и «почти на 50% сокращению требований к хранилищу». [58]
Java внутренне использует Modified UTF-8 (MUTF-8), в котором нулевой символ U+0000 использует двухбайтовую сверхдлинную кодировку 0xC0 , 0x80 , вместо просто 0x00 . [59] Модифицированные строки UTF-8 никогда не содержат фактических нулевых байтов, но могут содержать все кодовые точки Unicode, включая U+0000, [60] что позволяет обрабатывать такие строки (с добавленным нулевым байтом) традиционными строковыми функциями с нулевым завершением . Java считывает и записывает обычный UTF-8 в файлы и потоки, [61] но использует Modified UTF-8 для сериализации объектов , [62] [63] для Java Native Interface , [64] и для встраивания константных строк в файлы классов . [65] Формат dex, определенный Dalvik, также использует тот же модифицированный UTF-8 для представления строковых значений. [66] Tcl также использует тот же модифицированный UTF-8 [67], что и Java для внутреннего представления данных Unicode, но использует строгий CESU-8 для внешних данных. Все известные реализации модифицированного UTF-8 также обрабатывают суррогатные пары как в CESU-8 .
Язык программирования Raku (ранее Perl 6) использует utf-8
кодировку по умолчанию для ввода-вывода ( Perl 5 также поддерживает ее); хотя этот выбор в Raku также подразумевает «нормализацию в Unicode NFC (каноническая форма нормализации) . В некоторых случаях вы можете захотеть убедиться, что нормализация не выполняется; для этого вы можете использовать utf8-c8
«. [68] Этот вариант UTF-8 Clean-8 , реализованный Raku, является кодировщиком/декодером, который сохраняет байты как есть (даже недопустимые последовательности UTF-8) и допускает синтетические графемы нормальной формы. [69]
Версия 3 языка программирования Python рассматривает каждый байт недопустимого потока байтов UTF-8 как ошибку (см. также изменения с новым режимом UTF-8 в Python 3.7 [70] ); это дает 128 различных возможных ошибок. Были созданы расширения, позволяющие без потерь преобразовывать любую последовательность байтов, которая предположительно является UTF-8, в UTF-16 или UTF-32, транслируя 128 возможных байтов ошибки в зарезервированные кодовые точки и преобразуя эти кодовые точки обратно в байты ошибки для вывода UTF-8. Наиболее распространенный подход заключается в трансляции кодов в U+DC80...U+DCFF, которые являются низкими (конечными) суррогатными значениями и, таким образом, «недопустимыми» UTF-16, как это используется в подходе Python PEP 383 (или «surrogateescape»). [20] Другая кодировка, называемая MirBSD OPTU-8/16, преобразует их в U+EF80...U+EFFF в области частного использования . [71] В любом подходе значение байта кодируется в нижних восьми битах выходной кодовой точки. Эти кодировки необходимы, если недействительный UTF-8 должен пережить трансляцию в UTF-16, используемую внутри Python, и обратно, а поскольку имена файлов Unix могут содержать недействительный UTF-8, это необходимо для работы. [72]
Официальное название кодировки — UTF-8
, написание, используемое во всех документах Unicode Consortium. Дефис-минус обязателен, пробелы не допускаются. Вот некоторые другие используемые названия:
utf-8
часто используются. [ необходима ссылка ]utf8
и многие другие псевдонимы. [73]csUTF8
единственный псевдоним [74], который используется редко.UTF-8N
означает UTF-8 без метки порядка байтов (BOM), и в этом случае UTF-8
может подразумевать наличие BOM . [75] [76]65001
[77] с символическим именем CP_UTF8
в исходном коде.utf8mb4
, [78] тогда как utf8
и utf8mb3
относятся к устаревшему варианту CESU-8 . [79]AL32UTF8
[80] означает UTF-8, а UTF-8
означает CESU-8.18N
. [81]В различных документах по стандартам существует несколько текущих определений UTF-8:
Они заменяют определения, данные в следующих устаревших работах:
По своей общей механике они все одинаковы, а основные различия касаются таких вопросов, как допустимый диапазон значений кодовых точек и безопасная обработка недопустимых входных данных.
... на самом деле вы обычно просто предполагаете UTF-8, поскольку это, безусловно, самая распространенная кодировка.
Microsoft теперь по умолчанию сохраняет новые текстовые файлы как UTF-8 без BOM, как показано ниже.
Убедитесь, что ваш LayoutModification.json использует кодировку UTF-8.
Представление UTF-8 создается по требованию и кэшируется в объекте Unicode.
Устаревшие
и
члены реализации объектов Unicode на языке C были удалены в соответствии с PEP 623.
wstr
wstr_length
Visual Studio использует UTF-8 в качестве внутренней кодировки символов во время преобразования между исходным набором символов и набором символов выполнения.
InputStreamReader
иOutputStreamWriter
DataInput
иDataOutput
Ранее под XP (и, не проверено, но, вероятно, и под Vista тоже) циклы for просто не работали, пока была активна кодовая страница 65001