Группа разработчиков предлагает перейти на UTF-8

    Недавно на Hacker News опубликовали манифест программистов из Тель-Авива. Они предложили сделать UTF-8 решением по умолчанию для хранения текстовых строк в памяти и коммуникации.

    Материал породил активную дискуссию, и мы решили разобраться в ситуации, рассмотреть аргументы ИТ-экспертов — в том числе инженеров IBM и специалистов консорциума W3C.


    Фото — Raphael Schaller — Unsplash

    Ситуация с кодировками


    В 1988 году Джо Беккер (Joseph Becker) представил первый драфт стандарта Unicode. В основу документа легло предположение, что для хранения любого символа хватит 16 бит. Однако довольно быстро стало понятно, что этого недостаточно. Поэтому появились новые варианты кодировок — в том числе UTF-8 и UTF-16. Но разнообразие форматов и отсутствие строгих рекомендаций по их использованию привело к сумятице в ИТ-индустрии (в том числе в терминологии).

    Внутренний формат ОС Windows — это UTF-16. При этом авторы манифеста, обсуждение которого велось на Hacker News, говорят, что одно время в Microsoft упоминали термины Unicode и widechar в качестве синонимов для UTF-16 и UCS-2 (которая считается своеобразным предшественником UTF-16). Что касается экосистемы Linux, то в ней принято использовать UTF-8. Многообразие кодировок порой приводит к тому, что файлы повреждаются при передаче между компьютерами с различными ОС.

    Решением проблемы может стать стандартизация отрасли — переход на UTF-8 для хранения текстовых строк в памяти или на диске и обмена пакетами по сети.

    Почему UTF-8 считают лучше UTF-16


    Один из главных аргументов связан с тем, что UTF-8 сокращает объем памяти, занимаемый символами на латинице (их использует множество языков программирования). Латинские буквы, цифры и распространенные знаки препинания кодируются в UTF-8 лишь одним байтом. При этом их коды соответствует кодам в ASCII, что дает обратную совместимость.

    Также специалисты из IBM говорят, что UTF-8 лучше подходит для взаимодействия с системами, не ожидающими прихода многобайтных данных. Другие кодировки юникода содержат многочисленные нулевые байты. Утилиты могут посчитать их концом файла. Например, в UTF-16 символ A выглядит так: 00000000 01000001. В строке Си эта последовательность может быть обрезана. В случае с UTF-8 нулем является только NUL. В этой кодировке первая буква латинского алфавита представлена как 01000001 — здесь проблем с непредвиденным обрывом не возникает.

    По этой же причине инженеры из консорциума W3C рекомендуют использовать UTF-8 при разработке внешних интерфейсов. Так можно избежать сложностей с работой сетевых устройств.


    Фото — Kristian Strand — Unsplash

    Резидент Hacker News отметил, что UTF-8 позволяет отлавливать ошибки кодирования на ранних этапах. В ней байты читаются последовательно, а служебные биты определяют их количество. Таким образом, значение кодовой точки (code point) вычисляется однозначно и разработчикам приложений не нужно задумываться о проблеме Little-Endian или Big-Endian.

    Где у UTF-16 преимущество


    Буквы латинского алфавита и символы пунктуации могут занимать меньше памяти в UTF-8 (по сравнению с UTF-16). Некоторым кодовым точкам требуется одинаковое количество байтов в обеих кодировках — например, этот факт справедлив для греческого языка и иврита.

    Иначе обстоят дела с азиатскими иероглифами — в случае с UTF-8 им нужно больше места. Например, китайский символ будет представлен тремя байтами: 11101000 10101010 10011110. Этот же символ в UTF-16 будет выглядеть как 10001010 10011110.

    Что в итоге


    Дебаты, касающиеся проблемы внедрения единой кодировки, идут уже давно. Этот вопрос поднимался почти одиннадцать лет назад в треде на Stack Overflow. В нем принимал участие Павел Радзивиловский (Pavel Radzivilovsky) — один из авторов манифеста. С тех пор UTF-8 уже успела стать одной из самых популярных кодировок в интернете. И её признали обязательной для «всех ситуаций» в WHATWG — сообществе специалистов по HTML и API, развивающем соответствующие стандарты.

    С недавних пор в Microsoft тоже начали рекомендовать использовать UTF-8 при разработке веб-приложений. Возможно, в будущем эта практика распространится на другие утилиты.



    Что еще мы публикуем:

    «Прячь www»: почему разработчики мейнстрим-браузера снова отказались от отображения поддомена
    «Как мы строим IaaS»: материалы о работе 1cloud
    Ситуация: нарушают ли AdTech-компании GDPR?
    Эра 10-нм чипов — что ждет индустрию в будущем
    Компьютер, который отказывается умирать

    1cloud.ru
    IaaS, VPS, VDS, Частное и публичное облако, SSL

    Комментарии 69

      +7

      яЙНКЭЙН КЕР ОПНЬКН, Ю ЯОНП МХЙЮЙ МЕ ГЮЙНМВХРЯЪ

        +3

        Да вроде никто и не спорит. Везде UTF-8 стандарт в интернете, предлагают просто расширить применение.

          +2

          Почему-то вспомнилось "МЕЯ ВИДО?"

          +2
          Интересная была задумка с кодировками КОИ-8, но сегодня проблем со шрифтами нет.
            +2
            А я думал, она не из-за шрифтов применялась. А из-за того, что при пересылки по модему 8 бит могли обрезаться до 7, т.е. КОИ-8 превращался в ASCII
              0

              Мне, кстати, всегда было интересно, почему в пару V поставили Ж, а в пару W — В, ведь логичнее наоборот.

                0
                Это сделали ещё в 19-м веке для телеграфа. А потом постепенно перекочевало и в КОИ-8.
                Сейчас концов уже не найти наверное)

                Но наверное, для пары W — В есть логичная версия. В немецком языке именно буква «w» читается как «в». (В то время российские технари больше ориентировались на немецкий, а не английский).
                А «v» читается как «ф». Может, её по остаточному принципу привязали к «ж».
                  +1
                  Радисты тоже часто заменяют Ж на V.
                    +1

                    В это похоже и причина, в азбуке Морзе одна и та же последовательность соответствует и Ж и V

          –7
          Пусть JavaScript переделают. И Win API. И Foundation library на Mac. Тогда, собственно, и агитировать не понадобится.
            +2
            Ещё неплохо было бы все прошивки BIOS переписать, а также стандарты языков, где определены строки. Ещё сделать что-нибудь с COM портом, по которому я передаю строки на старенький принтер, умеющий печатать химстойкие этикетки. Сколько времени теперь будет передаваться одна буковка?
              0

              С принтером проще, он за драйвером спрятан.
              Если честно, в Макоси с NSString тоже просто, там её внутреннее устройство наружу почти не торчит.
              А что в BIOS? Я вообще не припомню в нём сколько-то значимых выходов за пределы ASCII, но я мог отстать от жизни.

                0
                > А что в BIOS? Я вообще не припомню в нём сколько-то значимых выходов за пределы ASCII, но я мог отстать от жизни.

                (U)EFI весь разработан на стандартах Windows, включая формат файлов (адаптированный PE), и в кодировках текстовых строк используется UTF-16LE, включая имена разделов в GPT, внутренние строки самой EFI…
                  0
                  Даже в старых биосах для P-4 встречался выбор языка, и китайский с японским там нередко присутствовали.
                    0
                    А ASCII — это не строки?
                    Что будет, если в С на котором пишут bios внезапно sizeof(char) станет UB, ведь символ в UTF8 не имеет задокументированного независящего от самого символа размера?
                    Да, использоваие stdint и прочих uint8_t немного спасёт (а в системном программировании я вообще ругаю людей, которые используют int), но, в любом случае, не все строковые литералы нуждаются в юникоде. Особенно в таком сыром, как utf-8.
                      0

                      ASCII – прост. Один символ – один байт, да ещё и старший бит не задействован.


                      Что касается сырости utf-8 – извините, ничего менее сырого нет. Ну кроме ASCII, но вам же мало будет?


                      А sizeof(char) не станет UB. Скорее вам запретят так просто разбирать строку на символы. В MacOS по сути давно спрятали это дело, чтобы узнать, как кодируется NSString – надо специально рыться в доках.

                        +3
                        А sizeof(char) не станет UB.
                        Какое вообще имеет отношение sizeof(char) ко всему этому?

                        Скорее вам запретят так просто разбирать строку на символы.
                        Не запретят. Строка в C/C++ вообще не имеет никакого отношения к работе с текстом. Она «заточена» под работу с байтами (да, вот так вот странно: тип char, в соотвествии со спецификацией, предназначен для работы с байтами, а вовсе не с символами… а тип std::string, соответственно, с последовательностями байт… добро пожаловать в странный и причудливый мир C/C++).

                        А для текста есть типы char8_t, char16_t, char32_t… ну и u8string, u16string, u32string… и «никто не уйдёт обиженным».

                        P.S. И нет — всё вышеописанное это не издевательство и не первоапрельская шутка, это то, что говорит стандарт. C++20 и следующий стандарт C (скорее всего C21). Стандарт ещё не принят, но N2231 в него уже согласились включить (это, собственно, почти единственное, по чему никаких разногласий не было).
                –4
                Хранение в мультибайтовой кодировке абсолютно всех строк, сильно замедлит обработку текста, особенно там где нужно вырезать, вставлять, сравнивать части строки.
                Такой проблемы нет с UTF-16.
                  +7

                  UTF-16 не панацея, в него тоже не все символы влазят. И будут у вас те же последовательности пар байт, только с заморочкой на big-endian/little-endian и свои особые "запрещённые" комбинации.
                  Там, где операции не зависят от кодировки (конечно же, с поправкой на общую корректность последоовательности), скорость будет примерно та же. Там, где нужно сравнивать-вырезать с учётом национальных алфавитов, суррогатных пар и др. дичи, UTF-16 сам по себе вам не поможет.

                    +1
                    Согласен, фиксированный размер одного символа всегда лучше. Строки — это не графические данные, например, и жаловаться на удвоение занимаемой памяти строками ну как минимум гоупо. А вообще это выглядит как — а давайте сломаем совместимость, но как бы чуть-чуть, не полностью, чтобы много позже всё же доломать и перейти на UTF-16 в итоге.
                      0

                      Там даже 4байт уже не хватает, чтобы все закодировать.

                        0
                        Да, в UTF-32 решили сделать обратную совместимость с UTF-16 и UTF-8 и ограничили количество символов/знаков до 1112064.
                        +6
                        Так ведь у UTF-16 размер одного символа тоже не фиксированный! Нет у UTF-16 никаких преимуществ. И у UTF-8 строго говоря их нет. Чистая вкусовщина.
                          0
                          Если в документе не используется несколько языков, а это, согласитесь, довольно редко, UTF-8 превратится сам по себе в 16 битный. Например документ на русском будет состоять сплошь из двух 8-ми битных пар.
                          Единственное исключение для для документа на английском, где UTF-8 будет эквивалентен ASCII-8.
                          Но в принципе, согласен. Вкусовщина.
                            0

                            Это просто частный случай. Возьмите любой текст со смайликами, или на арабском, тамильском. И, внезапно, окажется, что уже не всё так просто.

                              +2

                              В русском тексте используются не только буквы но и знаки. А они зачастую 8 бит в UTF-8.

                                +1
                                > Если в документе не используется несколько языков, а это, согласитесь, довольно редко, UTF-8 превратится сам по себе в 16 битный. Например документ на русском будет состоять сплошь из двух 8-ми битных пар.

                                «Я нашёл бо́льшую квартиру!»
                                «бо́льшую» — б — U+0431
                                о́ — U+043E U+0301
                                упс…
                                  +1

                                  "ё" то тоже неоднозначна.
                                  Мак её запишет как е с диерезисом.

                                    0

                                    А ещё бывают верхние и нижние индексы.
                                    Которые в определённом контексте вдруг становятся критически важными
                                    (привет, яндекс-транслейт)
                                    image

                                  +14

                                  Вот одно все же есть у UTF8 неоспоримое преимущество. Он обратно совестим с latin1. Поэтому если софт сам не меняет строчки, а просто их передает, то он автоматически может быть переведен на юникод.
                                  По этой причине в linux меньше проблем с кодировками чем в windows

                                0

                                Ровно те же проблемы, просто не так часто – поэтому про них периодически забывают и код ломается.
                                Вот как раз недавно чинил крэш из-за непарных суррогатов (приходили из браузера, не знаю уж, как юзеры умудрялись… JavaScript с этими строками нормально работал, а вот стандартная Маковская либа NSJSONSerialization падала).

                                  +6
                                  UTF-16 не является кодировкой с постоянным размером «символа». Например, популярные сейчас эмодзи всегда будут занимать в тексте 4 байта а не 2. Поэтому c UTF-16, точно так же как и с UTF-8, нельзя «вырезать, вставлять, сравнивать части строки» с помощью байтовых индексов.
                                    +2

                                    Никакой способ кодирования не способен решить все проблемы работы с текстом.
                                    Если бы думаете, что работа со строками — это работа с кодпоинтами, то вы ошибаетесь.
                                    Например, в юникоде существуют комбинированные символы — и вам придётся с ними работать.

                                      0

                                      Надо просто работать с графемами. Как это делает условный свифт

                                        0

                                        Это хороший подход. Правда, непонятно, что непечатными символами делать и с лигатурами.

                                      +4
                                      Нет, UTF-16 ничем не лечит, потому что:

                                      1. Этого лечения нет в любом UTF или UCS, потому что символ может занимать произвольное количество «кодовых пунктов» (UCP). Надо вам написать «бо́льшую», чтобы не путалось с «большу́ю» — пожалуйста: тут два символа по 2 UCP каждый, вторым идёт «COMBINING ACUTE ACCENT» (U+0301).
                                      Так что сразу избавьтесь от этих иллюзий, что можно сделать какой-нибудь p++ и перейти гарантированно на следующий символ.
                                      Более того, вы не можете сделать функцию вида step_left(wchar_t **pos), которая бы это отрабатывала надёжно во всех случаях, сдвигая pos — потому что на самом деле она должна быть step_left(wchar_t **pos, wchar_t *begin) — потому что может поступить на вход сломанный поток, в котором есть модификатор, но нет символа перед ним.

                                      2. Даже если бы вы рассматривали кодовые пункты, а не символы — пункты с кодами U+10000 и более представляются «суррогатами» в виде пар UTF-16 кодов.
                                        0
                                        Да, разобрался, спасибо.
                                        UTF-8 привлекает обратной совместимостью, это большой плюс.
                                        Остальные виды хранения не дают того удобства, ради которого задумывался Unicode изначально, даже UTF-32.
                                          0
                                          Комбинирующиеся символы есть уже в Unicode 1.0. А UCS-2 стала неактуальна с 1996го.

                                          После чего попытки насадить UTF-16 (вместо перехода на UTF-8) стали выглядеть несколько дико.

                                          Но ничего — прошло каких-то четверть века и разумное и естественное решение стало, наконец, мейнстримом даже в Windows…
                                          0
                                          Кстати, на C++ таки можно сделать p++, ходящий по таким сложносочинённым строкам. Только он может отказать – бросить exception, вернуть нулевой указатель или ещё что, в зависимости от реализации. Некорректные строки – куда больнее, чем просто символ из нескольких байт/слов (а обрабатывать их надо, они могут снаружи прийти).
                                            0
                                            Меня всегда больше интересовал вопрос: а чего, собственно, хотят добиться те программисты, которые считают, что от возможности адресовать строку посимвольно может быть какая-то польза. Ну вот рассмотрите две строки:
                                            Hello!
                                            Hello!
                                            Обе содержат по 6 символов (кодпоинтов, как хотите). Иии… что это нам даёт, собственно?

                                            Все алгоритмы работы с Юникодом, которые я видел, делились на два непересекающихся класса:
                                            1. Делающих что-то полезное.
                                            2. Индексирующих что-то «по номерам символов».

                                            Всё что я видел, обычно сводилось к подходу «для русского и латинницы работает, а на остальное нам наплевать».

                                            Но нельзя же с таким подходом выбирать кодировку для всех API и документов в операционке…
                                              0
                                              Основное это поиск подстроки в строке и замена части строки.
                                              Особое удовольствие доставит замена в ненормализованной строке.
                                                0
                                                Поиск подстроки и в UTF-8 и в UTF-16 строке отлично работает если индексировать байты — так что мимо, если строка ненормализована — ни черта всё равно работать не будет, независимо от кодировки.
                                                  +1
                                                  Да, я собственно об этом и говорю.
                                                  Строка для полноценной работы с ней нуждается в дополнительной индексации. И любые операции с ней будут опираться на эти индексы.
                                                  А посему, тип хранения уже становится не важен хоть UTF-8, -16 или -32, всё равно внутренне представление строки становится недоступным.
                                                    0
                                                    Да, при работе со строкой её нужно «делить на части». Но есть беда: деление на Unicode CodePoint — это одно из самых бессмысленных делений, какое можно себе представить.

                                                    Я вот не могу представить себе ни одного мало-мальски осмысленного алгоритма, где бы это представление мне помогло… но почему за него бьются со страшной силой… зачем? почему?
                                                      0
                                                      Найти-вырезать-вставить_другое, не рассматриваете?
                                                        0
                                                        Нет, потому что там, тоже, ну вот совершенно не проблема, что у вас «не все индексы одинаково полезны».

                                                        Есть, пожалуй, одно место, где от этого может быть польза. Возьмите ту самую статью про и с краткой. Вот в этой самой букве «й» кратаку можно убрать бекспейсом…

                                                        Но вот ради этого, одного единственного, места в редакторе — всё с ног на голову переворачивать?
                                        +1
                                        В основу документа легло предположение, что для хранения любого символа хватит 16 бит. Однако довольно быстро стало понятно, что этого недостаточно. Поэтому появились новые варианты кодировок — в том числе UTF-8 и UTF-16.

                                        UTF-8 и UTF-16 не имеют отношения к тому, что «для хранения любого символа не хватает 16 бит».

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

                                        да, но…
                                        Другие кодировки юникода содержат многочисленные нулевые байты. Утилиты могут посчитать их концом файла.

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

                                        самое забавное и интересное в статье — то, что мелкософт рекомендует использовать старые A-функции. я пропустил эту новость. эти функции считались пережитком прошлого, тяжелым наследием windows 95. и вот теперь это снова стильно, модно, молодежно. правда, как я понял, для всего этого великолепия нужен (относительно) свежий билд десятки.
                                          0

                                          Что-то сложно пока что представить сферу применения вот этого вот, кроме опенсорса, где можно любые правила диктовать.
                                          https://docs.microsoft.com/en-us/windows/uwp/design/globalizing/use-utf8-code-page

                                            +1
                                            Вспоминая Джоэла, новая команда разработчиков Microsoft обнаружила в *W функциях фатальный недостаток. :)
                                            +1
                                            Недавно на Hacker News опубликовали манифест программистов из Тель-Авива.

                                            А можно более точную ссылку чем «на Hacker News» указывать? если пользователь активно следит за HN — эта статья не особо полезна. Если не следит — то получается непонятно.
                                              0
                                              странно, а что, есть много языков программирования, где пишут не латиницей (которая в ASCII совместима с utf8)?
                                                0
                                                Ви таки не поверите

                                                Добро пожаловать в XXI век!
                                                  0
                                                  кроме C++ еще можно в JavaScript и PHP
                                                    +1

                                                    Python3:


                                                    >>> def йа_криведко():
                                                    ...    превед = True
                                                      0
                                                      Есть ещё use utf8; в perl и {-# LANGUAGE UnicodeSyntax #-} в Haskell.

                                                      Так что, похоже, вопрос нужно ставить так: а остались ли вообще ещё популярные языки, где это невозможно?
                                                      0
                                                      Автора Unicode упомянули, а создателей UTF-8: Кена Томпсона и Роба Пайка, — забыли почему-то.
                                                        0
                                                        И что они конкретно предлагают? UTF-16 к WinAPI уже гвоздями прибит.
                                                          0

                                                          Так вроде уже добавили поддержку UTF-8.

                                                            0
                                                            Ага. Только… как тут, чуть выше, указывали: Windows Version 1903 (May 2019 Update).

                                                            Сколько там осталось? 3 года до окончания поддержки Windows 8.1? Вот тода и можно будет в коммерческих проектах использовать.

                                                            Ну а пока — можно только в своих пет-проектах начинать, да…
                                                              0
                                                              Ну, такое себе. Венду на плаву давно уже давно только многодесятилетняя обратная совместимость держит. Если её закапывать, то и правда целесообразнее будет MS Linux выпускать. Разумеется с аналогом Wine из коробки.
                                                                0
                                                                Разумеется с аналогом Wine из коробки.

                                                                Если уж забивать, так забивать. Скорее драйверы запилят норм и свой аналог вейланд или чего-то подобного и самобытного.

                                                              0
                                                              Предлагают вспомнить, что то, что «прибито гвоздями» — можно гвоздодёром и исправить…
                                                              +1
                                                              Мне казалось этот вопрос давно уже решен.
                                                                0
                                                                Например, в UTF-16 символ A выглядит так: 00000000 01000001. В строке Си эта последовательность может быть обрезана. В случае с UTF-8 нулем является только NUL. В этой кодировке первая буква латинского алфавита представлена как 01000001 — здесь проблем с непредвиденным обрывом не возникает.

                                                                Читал я это, читал. И так, и эдак. Смотрел в источники по ссылкам. Но так и не понял, откуда берет своё начало утверждение и какую смысловую нагрузку оно несет: "В случае с UTF-8 нулем является только NUL". Может кто-то пояснить?

                                                                  +3
                                                                  Имелось в виду, конечно, «в случае с UTF-8 нуль содержится только в NUL». Хотя всё равно может быть чуть-чуть непонятно. Нужно понять какой именно NUL имеется в виду. Речь идёт про вот этот вот NUL. А фраза означает следующее: из всех Unicode-символов (Code Point, если строго) в UTF-8 представлении нулевой байт содержится только в NUL.

                                                                  Соответственно функции, которые считают этот самый NUL концом строки (это соглашение языка C) могут принять строку в UTF-8 представлении — и если им не нужно работать с символами (а на практике функций, которые просто передают строку откуда-то куда-то на порядок, а то и на два больше, чем всех остальных), то они продолжат прекрасно работать…

                                                                  Более того, масса других функций — тоже будут отлично работать. Например если у вас протокол использует служебные слова в US-ASCII (а таких тоже — больше, чем вы думаете), то всё тоже продолжит работать с UTF-8. Потому что US-ASCII в UTF-8 — имеет то же битовое представление… а символы вне US-ASCII — имеют в старшем разряде единичку в каждом байте (то есть никак не могут совпасть ни с одним US-ASCII символом).
                                                                  0

                                                                  «Лучше всего» в Эрланге сделано: строка — это односвязный список int32. Т.е. на каждый символ идет 8 байт: 4 на unicode-code-point (прямо в голом виде, никакая кодировка не нужна) и еще 4 на указатель на следующий символ-элемент списка. Расточительно, но никто не парится и все используют.

                                                                    0
                                                                    «Идеологически» то же самое во многих языках, включая Haskell. Но компилятор может (и не только теоретически) это дело заоптимизировать. Так что в результате, в большинстве случаев, программы работают быстрее (и требуют меньше памяти), чем если бы речь шла про Python или PHP.
                                                                    0
                                                                    Может ещё и год представлять не двумя знаками?                       /irony 

                                                                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                    Самое читаемое