Как стать автором
Обновить

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

Время на прочтение3 мин
Количество просмотров14K
Всего голосов 26: ↑21 и ↓5+23
Комментарии69

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

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

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

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

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

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

НЛО прилетело и опубликовало эту надпись здесь
Радисты тоже часто заменяют Ж на V.

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

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

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

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

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

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


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


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

А 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 в него уже согласились включить (это, собственно, почти единственное, по чему никаких разногласий не было).
Хранение в мультибайтовой кодировке абсолютно всех строк, сильно замедлит обработку текста, особенно там где нужно вырезать, вставлять, сравнивать части строки.
Такой проблемы нет с UTF-16.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Нет, 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 кодов.
Да, разобрался, спасибо.
UTF-8 привлекает обратной совместимостью, это большой плюс.
Остальные виды хранения не дают того удобства, ради которого задумывался Unicode изначально, даже UTF-32.
Комбинирующиеся символы есть уже в Unicode 1.0. А UCS-2 стала неактуальна с 1996го.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Python3:


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

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

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

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

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

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

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

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

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

Имелось в виду, конечно, «в случае с 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 символом).

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

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