LINUX.ORG.RU

Написал статью «Как жить если у вас юникод»

 ,


5

4

Собственно, сабж. Статья про то самое, что мы с Eddy_Em не могли осилить в прежние времена. В этом году я это, внезапно, осилил. Ну и написал статью.

https://saahriktu.ru/pdf/kak_jit_esli_u_vas_yunikod.pdf

★★★★★
Ответ на: комментарий от no-such-file

Большая и маленькая разный смысл имеют. Например, когда пишешь про ПРО.

В 8-битной кодировке æ не лигатура, а буква некоторых европейских языков. Еë нельзя менять на ae.

monk ★★★★★
()
Ответ на: комментарий от monk

Нужен, например, индекс по тэгам с кнопками типа «Ab..Af»

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

pasquale
()
Ответ на: комментарий от pasquale

например для удаления диакритики

Нельзя удалять. Вот эти также удалили перед отбором для страницы:

https://classes.ru/all-vietnamese/dictionary-vietnamese-russian.htm?letter=2

https://classes.ru/all-vietnamese/dictionary-vietnamese-russian.htm?letter=3

И обе страницы стали одинаковыми.

Даже в русском мы ожидаем увидеть йод по алфавиту после иприта, а не наоборот.

monk ★★★★★
()
Ответ на: комментарий от monk

Кстати, пример с æ хороший. Первые три буквы слова «præsidium» должны быть «pra» (латинский). А первые три буквы слова «lærer» должны быть «lær» (датский).

То есть, помимо сортировки и смены регистра строки, необходима ещё и зависимая от языка функция взятия подстроки.

monk ★★★★★
()
Ответ на: комментарий от hateyoufeel

хранить mpi рядом, покупка ceil, продажа floor, разницу в карман, делов то. А то пойдут потом инкременты по 0.000375 и будете тоже костыли прикручивать

а деноминатор в uint64 конечно сильно

arcanis ★★★★
()
Ответ на: комментарий от monk

Кстати, пример с æ хороший. Первые три буквы слова «præsidium» должны быть «pra» (латинский).

Всё ещё грустнее. æ в юникоде называется лигатурой (aelig), но декомпозиция для неё не прописана.

https://dencode.com/string/unicode-normalization?v=pr%C3%A6sidium

monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от saahriktu

Т.е. просто загоняют строки в UTF-8 в char* строки и дальше просто их выводят.

Для того и был сделан UTF-8

Не надо на *nix тащить вантузоидный wchar.

И более того, даже UTF-32 тащить тоже не надо, не поможет – ни тот, ни другой не обеспечат O(1) доступа к N-ному символу в строке.

Создатели UTF укурились со своими модификаторами и эмодзями.

timdorohin ★★★★
()
Ответ на: комментарий от timdorohin

UTF-8 - это кодировка. char* - это просто массив байтов. В то время как wchar_t* - это массив кодепоинтов и ничего виндузятного в нём нет.

wchar_t — это тип данных стандарта ANSI/ISO C

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от monk

æ в юникоде называется лигатурой (aelig), но декомпозиция для неё не прописана.

Потому что лигатурой она была в латыни, а теперь это просто буква.

Проблемы с сортировкой символов (в Юникоде или нет) решаются не кодировкой, а таблицами сортировки, индивидуальными для каждого языка.

pasquale
()
Ответ на: комментарий от pasquale

Потому что лигатурой она была в латыни, а теперь это просто буква.

Есть ещё кириллическая ӕ. Название «Cyrillic Small Ligature a Ie». Но декомпозиции тоже нет.

Проблемы с сортировкой символов (в Юникоде или нет) решаются не кодировкой, а таблицами сортировки, индивидуальными для каждого языка.

Сортировка и поиск в юникоде работают. Не работает получение или замена подстроки.

monk ★★★★★
()
Ответ на: комментарий от ComradeDOS

то легко можно похерить данные

Как? Если работать с ним как с массивом байт, то ничего ему не будет даже если на один символ приходится несколько байт. Также корректно будет работать простой парсинг, например разделение на слова по запятой.

X512 ★★★★★
()
Ответ на: комментарий от X512

Нет, именно массив кодепоинтов. Просто на один кодепоинт на разных платформах может выделяться разное кол-во байт. Не более того.

На данный момент значения кодепоинтов спокойно умещаются в 2 байта. Поэтому разницы, по сути, вообще нет. Кроме того, что в GNU/Linux'е этот массив просто в 2 раза больше оперативки сожрёт чем в винде. Вот и вся разница.

saahriktu ★★★★★
() автор топика
Последнее исправление: saahriktu (всего исправлений: 1)
Ответ на: комментарий от X512

Очень хороший пример разработки кроссплатформного API - core Harbour.
Функционирует: любом дистрибутиве Linux, Windows, OS2, MAC, ..., ...
Так вот в core имеется API для использования: code page, UNICODE и wchar в частности.
Так что у тех кто «умеет» всё работает.
UNICODE был создан для возможности использования в строках символов разной кодировки.
Предпочитаю использовать code page.
Конечно имеется API для использования UNICODE, но в алгоритмах строки лишь в CHAR или WCHAR.
UTF-8 это «Горе от ума».

Forum0888
()
Ответ на: комментарий от saahriktu

Просто на один кодепоинт на разных платформах может выделяться разное кол-во байт.

Кодпойнт – это целое число, там нет никаких байт. В Windows wchar_t – это UTF-16, а не кодпойнт.

На данный момент значения кодепоинтов спокойно умещаются в 2 байта.

Уже давно как нет. Смайлы тому самый наглядный пример. В некоторых языках, например китайском для написания некоторых слов требуются символы не влезающие в 2 байта.

X512 ★★★★★
()
Ответ на: комментарий от Forum0888

Очень хороший пример разработки кроссплатформного API - core Harbour.

Оно понимает символы, не влезающие в 2 байта? Если нет, то не нужно.

UTF-8 это «Горе от ума».

Вот UTF-16 – это настоящее горе от ума, сочетающее недостатки всех кодировок одновременно.

X512 ★★★★★
()
Ответ на: комментарий от Forum0888

Тип wchar_t является определяемым реализацией расширенным типом символов. В компиляторе Майкрософт он представляет 16-разрядный символ, используемый для хранения Юникода в кодировке UTF-16LE, собственного типа символов в операционных системах Windows.

Что я собственно и написал.

X512 ★★★★★
()
Ответ на: комментарий от X512

В Windows wchar_t – это UTF-16, а не кодпойнт.

Если это так, то в винде принципиально несовместимая реализация.

У нас в GNU/Linux по стандарту ANSI C wchar_t - это число. Его синонимом в glibc является wint_t.

Уже давно как нет.

Ну, значит, наши 4 байта как раз вовремя. Значит, так и надо.

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от saahriktu

Толку мало от 4 байт. Составные символы всë равно в них не влазят, а обработка строк в несколько раз больше памяти требует, чем в utf8. Если достаточно поиска, сортировки и вывода, лучше utf8. Если нужно редактирование, то массив графем.

monk ★★★★★
()
Ответ на: комментарий от monk

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

А когда данные в виде отдельных байтов, то там пока ещё непонятно какой байт к какому кодепоинту относится. И нужно сначала ещё собирать из этих байтов кодепоинты, и только после этого можно переходить к дальнейшим шагам. А тут эту работу выполнять уже не требуется.

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от saahriktu

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

На уровне байтов UTF-8 можно разбивать на строки. Делить на поля по «:», даже делать синтаксический разбор для языка программирования.

А когда данные в виде отдельных байтов, то там пока ещё непонятно какой байт к какому кодепоинту относится.

А когда данные в виде отдельных кодепоинтов, то там непонятно, какой кодепоинт к какому символу относится.

И нужно сначала ещё собирать из этих байтов кодепоинты

Не обязательно. Иногда достаточно получить границы символов, а сам символ можно в UTF-8 весь (все его кодепоинты) хранить.

monk ★★★★★
()
Ответ на: комментарий от monk

Конечно, можно работать со строками и без таких приближений, но задачи бывают разные.

А когда данные в виде отдельных кодепоинтов, то там непонятно, какой кодепоинт к какому символу относится.

А это уже другой вопрос. И, повторяю, это зависит от конкретных данных. Множество символов занимают ровно один кодепоинт. Но есть и составные из нескольких кодепоинтов, да.

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от saahriktu

Множество символов занимают ровно один кодепоинт.

Ага. И множество программ «поддерживающих Юникод» работают только с такими символами (и при чтении строки сразу NFC-нормализацию делают).

Также как множество программ для UNIX постулируют, что в именах файлов, которые им встретятся, невозможны пробелы, переносы и управляющие символы.

Можно, конечно, и так. Но потом такое отношение кончается чем-то вроде:

Из-за того что оператор французской компании в плане полёта поставил лишние точки, в Британии произошёл масштабный сбой в системе управления полётами. В понедельник (28.08.2023) были отменены 1585 рейсов, во вторник - 345, в среду - 64.

monk ★★★★★
()
Ответ на: комментарий от monk

Есть ещё кириллическая ӕ

Аналогично, это буква, а не лигатура.

Не работает получение или замена подстроки.

Ну вы же хотите locale-dependent поведения - считаете, что æ - это две буквы, а не одна. В однобайтной кодировке вы тоже не получите «pra» из «præ». Для составления индекса строку нужно привести к collation symbols и выбрать символы с первичными весами.

pasquale
()
Ответ на: комментарий от pasquale

В однобайтной кодировке вы тоже не получите «pra» из «præ».

В однобайтной лигатуры из нескольких символов собираются. Средствами текстового процессора (TeX, HTML и прочее). А здесь ни два ни полтора.

monk ★★★★★
()
Ответ на: комментарий от monk

Можно, конечно, и так. Но потом такое отношение кончается чем-то вроде

Всё больше чем отсутствие поддержки юникода.

Остаётся надеяться на переизобретение улучшенного Юникода 2.0 уже без обратной совместимости с тем, что казалось нормальным в начале 1990-х.

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от monk

В однобайтной лигатуры из нескольких символов собираются. Средствами текстового процессора

Символы æ и fi - те же самые символы как в Юникоде, так и в однобайтных кодировках (ISO-8859-1 и MacRoman соответственно). Ни один из этих символов не является лигатурой ни в Юникоде, ни в однобайтных кодировках.

pasquale
()
Ответ на: комментарий от saahriktu

Остаётся надеяться на переизобретение улучшенного Юникода 2.0

Невозможность сочетания букв с диакритическими знаками не является улучшением (и, к счастью, изобретено не будет.)

pasquale
()
Ответ на: комментарий от pasquale

Ни один из этих символов не является лигатурой ни в Юникоде, ни в однобайтных кодировках.

В MacRoman являлись: https://sebastienguillon.com/test/jeux-de-caracteres/MacRoman_to_utf8.txt.php

В ISO-8859-1 æ не лигатура, а буква датского языка.

По нормальной схеме, должно было быть два символа юникода. Но устроили унификацию как с CJK. При том, что украинцам и белорусам отдельную i сделали.

monk ★★★★★
()
Ответ на: комментарий от pasquale

Невозможность сочетания букв с диакритическими знаками не является улучшением (и, к счастью, изобретено не будет.)

Буквы с диакритическими знаками сочетаются выделением символа с диакритикой.

А невозможность написать у̸͚̬̥͕̭̭͙̖̱̻̺̟͉͚̹̮̖̯̻̺͇̫̺̟̓̓̀̕͝ж̸̜̭̯̰̟̟̙̝̫͎͈̩͉̞̤̤̺̼̬̀̕͝а̸̨̢̢̛̛̛̩̣̦͉͔̼̳̣̻͍̭̭̎̽̅̌̐͋̈́́̊͂̀͌̿̋̈͌̊̓̽͊̀̒̉̇̑̏̈́͆́̇̏͒́̇̕̚̚͝ͅс̸̧̧͇̳̹͓͇͖̜̠͉͉̫̅̒͒̐̇̂͗̃̊̅͐͑̉͒̋͐́́̿̐̀̌̓͒̕̚̚̕ы̷̡̧̧̨̡̰̗̱̭̻̱͚̫̳̯̯̟̰̤̖̪̦͚̱̖̳̖̘͈̫̫͔͚̹̫͖̺̮̪̦͒͛͛̚͜ͅͅ ̵̨͇̺̞͎̣̹̬̰͉͛͗̑̔́̈́͑͒̄͛̒͗̂͋̈́̉̍͜͝͠͠ю̴̛͎̣̹͈̖̱̹̙͙̘̆̂͂̀̌̽̍̑͆̓̈́̌̒͗̀͒̿͂͆̃̆̔̓̌́͒͛̕̕̕̚͝͠͝͠н̸̨̛̖̫̯̺͇̺͈͍̘̱̕̚и̸̢̱͇̦̘̖̜͈̹̠̭̠͇̱̲͇̼̹̣̲̙͎͉̏̊̎̑͘͜͠ͅк̶̢̹̟̫̞̙̺̤̙̬̻̳͇̜͔̗͈̭̻̭͍̝͙͈͕̦͕̺̈́͂̓͌̒͐̎̅͆͂̒̑̆̓̀̄̒͛͋̎̀̑̋̏̒̾̄́̃͐͂͋̄̈́̈́͑͝͝о̵̡̡̧̧̛̗̣̼͖̘̠͎͚̟̻̠̖̗̖̼͙͎͓͉͖̲̬̓̽̊̀͊͐͋̈́͐̂͒̓͝͠͝д̵̡̨̛͖͚͚̻͔͓͚̫̭͕͚̻̪̝͕͇̺̮̮͓͎͕̋͆̋̅̏̃̈́̀̓́͑͌̈͑̀̅̓͆̆́̈́́̇̏͌͋̓̇̇̉̇͛̈̈̊̄̈́͘͝͠ӓ̸̛̛͔̪͖̘̰͚͕̬̹̣̖̦́̓̎̊̇̈́͗͛͒̓̄̐̒̆̉͌̀̎̈́̐͗̔̆̂͋̍̈́͌̈̈́̈́́̾̚͠ является заметным улучшением.

monk ★★★★★
()
Ответ на: комментарий от pasquale

В однобайтной кодировке вы тоже не получите «pra» из «præ».

В однобайтной кодировке как раз легко прописать все исключения: например, чтобы при нажатии Backspace происходило преобразование æ → a, fi → f и т.д. Но в юникоде символов слишком много, чтобы перебрать все вручную и в ICU нет метода для автоматической обработки лигатур.

monk ★★★★★
()
Ответ на: комментарий от X512

Какой смысл работать с многобайтовой кодировкой как с массивом байт (только если не реализовывать свой парсер для неё)?

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

Ну собственно, и всё.) Преобразование регистра уже работать не будет для символов выходящих за границы набора ASCII, подсчёт количества символов тоже не даст вменяемого результата и так далее.

ComradeDOS
()
Ответ на: комментарий от ComradeDOS

Какой смысл работать с многобайтовой кодировкой как с массивом байт (только если не реализовывать свой парсер для неё)?

А какой смысл работать как с массивом кодепоинтов?

Ну собственно, и всё.

Это достаточно много. Скажем, парсер языка программирования типа Java можно целиком делать, не выделяя кодепоинты в идентификаторах.

подсчёт количества символов тоже не даст вменяемого результата и так далее

Вот это для кодепоинтов тоже справедливо.

monk ★★★★★
()
Ответ на: комментарий от ComradeDOS

Дык это вполне логично, что у тебя похожие символы в таблице не хранятся, а хранится базовый символ и диакритический знак.

Есть редакторы, где осилили по Backspace удалять символ целиком (vim, gedit, …).

monk ★★★★★
()
Ответ на: комментарий от monk

В core Harbour интересный/хороший API для использования code page.
При инициализации core создаются объекты, содержащие кодовые страницы.
При этом для каждой кодовой страницы предусмотрена возможность использования метаданных о символах.
Далее код из моего API для использования кодовых страниц

// --- Данные о кодовой странице
//
typedef struct CODEPAGE__ {

  char            *ID;                                       // ID code page /название/
  char            *Info;                                     // Комментарии о назначении кодовой страницы
  UNITABLE_       *pUnicodeTable;                            // Ссылка к UNICODE table
  BYTE            *FlagsChars;                               // Флаги символов
  BYTE            *FlUpperChar;                              // Upper символы
  BYTE            *FlLowerChar;                              // Lower символы
  BYTE            *Sort;                                     // Sort  символов
  BYTE            *Acc;                                      // 
  int             cntACSort;                                 // 
  int             Type;                                      // 

// --- Функции для работы с TCHAR символами
//
  CDP_GET_FUNC          FuncWcharGet;                        // Адрес функции, производящей получение символа
  CDP_PUT_FUNC          FuncWcharPut;                        // Адрес функции, производящей запись символа
  CDP_LEN_FUNC          FuncWcharLen;                        // Адрес функции, возвращающей размер символа
  CDP_UPPER_FUNC        FuncWcharUpper;                      // Адрес функции для конвертирования символа к upper
  CDP_LOWER_FUNC        FuncWcharLower;                      // Адрес функции для конвертирования символа к lower
  CDP_FLAGS_FUNC        FuncWcharFlags;                      // 
  CDP_CMP_FUNC          FuncWcharCmp;                        // Адрес функции для сравнения символов
  CDP_CMP_FUNC          FuncWcharCmpI;                       // Адрес функции для сравнения символов без использования case

  int                   cntMulti;                            // 
  int                   cntMultiUC;                          // 
  META_MULTIchar_       *pMultiChar;                         // 

  void                  *Buffer;                             // 
  CODEPAGE__            *NextCodePage;                       // Ссылка к следующей code page

} CODEPAGE_;                                                 // typedef struct CODEPAGE__ {

Так вот предусмотрены таблицы FlagsChars, ... которые API весьма эффективно позволяют работать с символами.

А теперь ради интереса посмотрите API ITU.

--------------------------------------------------
Вообщем то давно имеется желание с использованием API для работы с метаданными разработать кроссплатформенное API для locale.
Одна из фич будет в том, что его можно будет использовать в любой ОС.

О многом не сказал

Forum0888
()
Последнее исправление: Forum0888 (всего исправлений: 4)
Ответ на: комментарий от pasquale

Кстати, у нынешнего Юникода есть ещё один неустранимый недостаток. Какой гений решил, что порядок сортировки строк должен определяться национальностью пользователя?

Почему, например, для сортировки шведских песен по алфавиту я должен перезапускать весь GNOME Files c LC_COLLATE=sv_SE.UTF-8. А если в одном каталоге есть русские и шведские имена, то задача сортировки по алфавиту вообще неразрешима.

monk ★★★★★
()